Class: Og::KirbyStore

Inherits:
SqlStore show all
Defined in:
lib/og/store/kirby.rb

Overview

A Store that persists objects into a KirbyBase database. To read documentation about the methods, consult the documentation for SqlStore and Store.

Constant Summary collapse

Text =
:Text

Instance Attribute Summary

Attributes inherited from SqlStore

#conn

Attributes inherited from Store

#options, #transaction_nesting

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from SqlStore

#delete_all, #enable_logging, #managed_tables, #select, #select_one, #type_cast, #unmanaged_tables, #update, #update_by_sql, #update_properties

Methods included from SqlUtils

#blob, #build_join_name, #date, #escape, #join_class_ordering, #join_object_ordering, #join_table, #join_table_index, #join_table_info, #join_table_key, #join_table_keys, #ordered_join_table_keys, #parse_blob, #parse_boolean, #parse_date, #parse_float, #parse_int, #parse_timestamp, #quote, #quote_array, #table, #tableize, #timestamp

Methods inherited from Store

create, #delete, for_name, #force_save!, #insert, #save, #transaction, #update, #update_properties

Constructor Details

#initialize(options) ⇒ KirbyStore

Returns a new instance of KirbyStore.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/og/store/kirby.rb', line 45

def initialize(options)
  super
  
  @typemap = {
    Integer => :Integer,
    Fixnum => :Integer,
    Float => :Float,
    String => :String,
    Time => :Time,
    Date => :Date,
    TrueClass => :Boolean,
    FalseClass => :Boolean,
    Object => :Object,
    Array => :String,
    Hash => :String
  }
  
  mode = options[:mode] || :local
  
  if mode == :client
    # Use a client/server configuration.
    @conn = KirbyBase.new(:client, options[:address], options[:port])
  else
    # Use an in-process configuration.
    base_dir = self.class.base_dir(options)
    FileUtils.mkdir_p(base_dir) unless File.exist?(base_dir)
    @conn = KirbyBase.new(
                          :local, 
                          nil, nil, 
                          base_dir, 
                          options[:ext] || '.tbl'
                          )
  end
end

Class Method Details

.base_dir(options) ⇒ Object

Override if needed.



32
33
34
# File 'lib/og/store/kirby.rb', line 32

def self.base_dir(options)
  options[:base_dir] || './kirbydb'
end

.destroy(options) ⇒ Object



36
37
38
39
40
41
42
43
# File 'lib/og/store/kirby.rb', line 36

def self.destroy(options)
  begin
    FileUtils.rm_rf(base_dir(options))
    super
  rescue Object
    Logger.info 'Cannot drop database!'
  end
end

Instance Method Details

#aggregate(term = 'COUNT(*)', options = {}) ⇒ Object Also known as: calculate

– FIXME: optimize me!

++



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/og/store/kirby.rb', line 158

def aggregate(term = 'COUNT(*)', options = {})
  case term.upcase
  when /COUNT/
    count(options)
  when /MIN/ 
    term =~ /MIN\((\w*)\)/
    find_column_values($1, options){|result| result.min}
  when /MAX/
    term =~ /MAX\((\w*)\)/
    find_column_values($1, options) {|result| result.max}
  when /AVG/
    term =~ /AVG\((\w*)\)/
    values = find_column_values($1, options) do |results|
      results.inject(0.0){|sum, result| sum + result} / results.size
    end
  when /SUM/
    term =~ /SUM\((\w*)\)/
    values = find_column_values($1, options) do |results|      
      results.inject(0.0){|sum, result| sum + result}
    end
  end
end

#closeObject



80
81
82
83
# File 'lib/og/store/kirby.rb', line 80

def close
  # Nothing to do.
  super
end

#commitObject

Commit a transaction.



291
292
293
# File 'lib/og/store/kirby.rb', line 291

def commit
  # nop, not supported?
end

#convert_to_instance(klass, record) ⇒ Object

convert the given record into an instance of the given class. Supports schema_inheritance.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/og/store/kirby.rb', line 105

def convert_to_instance(klass, record)
  return nil unless record
  
  if(klass.schema_inheritance?)
    obj = eval("#{record.ogtype}.allocate")
  elsif
    obj = klass.allocate
  end

  record.each_pair do |k, v|
    assign_sym = (k.to_s + '=').to_sym
    if (v && obj.respond_to?(assign_sym))
      obj.method(assign_sym).call(v)
    end
  end
  obj
end

#count(options) ⇒ Object

– FIXME: optimize me! ++



149
150
151
# File 'lib/og/store/kirby.rb', line 149

def count(options)
  find(options).size
end

#enchant(klass, manager) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/og/store/kirby.rb', line 85

def enchant(klass, manager)
  klass.send(:attr_accessor, :oid)
  klass.send(:alias_method, :recno, :oid)
  klass.send(:alias_method, :recno=, :oid=)
  
  super
end

#find(options) ⇒ Object



131
132
133
134
135
136
137
138
139
# File 'lib/og/store/kirby.rb', line 131

def find(options)  
  #FIXME: this currently overrides options with scope; this should
  #work the other way around
  if scope = options[:class].get_scope
    scope = scope.dup
    options.update(scope)
  end
  query(options)
end

#find_column_values(column_name, options) ⇒ Object

find an array of values for the given column; or a hash of arrays if there is a group by expression. Takes an optional block that operates on each value in the array.



186
187
188
189
190
191
192
193
194
195
196
# File 'lib/og/store/kirby.rb', line 186

def find_column_values(column_name, options)
  options[:column] = column_name
  results = find(options)
  if (block_given?)
    if (options[:group]) #hash of arrays
      results.values.collect { |group| yield group}
    else
      yield results
    end
  end
end

#find_one(options) ⇒ Object



141
142
143
# File 'lib/og/store/kirby.rb', line 141

def find_one(options)
  query(options).first
end

#get_key(obj) ⇒ Object

retrieve the join table key name for the object



319
320
321
322
# File 'lib/og/store/kirby.rb', line 319

def get_key(obj)
  obj.class.name =~ /::(\w*)/
  ($1.downcase + '_oid').to_sym
end

#get_table(klass) ⇒ Object



93
94
95
# File 'lib/og/store/kirby.rb', line 93

def get_table(klass)
  @conn.get_table(klass.table.to_sym)
end

#join(obj1, obj2, table, options = nil) ⇒ Object

FIXME: this method relies on naming conventions and does not account for more than one set of ‘::’ marks.



308
309
310
311
312
313
314
315
# File 'lib/og/store/kirby.rb', line 308

def join(obj1, obj2, table, options = nil)
  first, second = join_object_ordering(obj1, obj2)
  
  options[get_key(first)] = first.pk
  options[get_key(second)] = second.pk
  
  @conn.get_table(table.to_sym).insert(options)
end

#load(pk, klass) ⇒ Object Also known as: exist?

:section: Lifecycle methods.



99
100
101
# File 'lib/og/store/kirby.rb', line 99

def load(pk, klass)
  convert_to_instance(klass, get_table(klass)[pk.to_i])
end

#query(options) ⇒ Object

needs refactoring, badly



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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
# File 'lib/og/store/kirby.rb', line 199

def query(options)
  Logger.debug "Querying with #{options.inspect}." if $DBG
  
  klass = options[:class]
  
  table = get_table(klass)
  result_set = []
  if (klass.schema_inheritance_child?)
    type_condition = 'ogtype = \'' + klass.name + '\''
    condition = options[:condition]
    
    if (condition)
      #if there is already an ogtype condition do nothing
      if (condition !~ /ogtype/)
        options[:condition] = condition + " and #{type_condition}"
      end
    else
      options[:condition] = type_condition
    end
  end
  
  #FIXME: add support for select statements with mathematics (tc_select)
  #as well as 'from table' statements
  if condition = options[:condition] || options[:where]
    condition = condition.is_a?(Array) ? prepare_statement(condition) : condition
    condition.gsub!(/ogtc_resolveoptions\w*\./, '')
    condition.gsub!(/([^><])=/, '\1==')
    condition.gsub!(/ OR /, ' or ')
    condition.gsub!(/ AND /, ' and ')
    condition.gsub!(/LIKE '(.*?)'/, '=~ /\1/')
    condition.gsub!(/\%/, '')
    condition.gsub!(/(\w*?)\s?([=><])(.)/, 'o.\1 \2\3')
    condition.gsub!(/o.oid/, 'o.recno')
    if(condition =~ /LIMIT (\d+)/)
      condition.gsub!(/LIMIT (\d+)/, '')
      options[:limit] = $1.to_i
    end
    result_set = eval("table.select do |o| #{condition} end")
  else
    result_set = table.select
  end
  
  if (order = options[:order])
    order = order.to_s
    desc = (order =~ /DESC/)
    order = order.gsub(/DESC/, '').gsub(/ASC/, '')
    eval("result_set.sort { |x, y| x.#{order} <=> y.#{order} }")
    result_set.reverse! if desc
  end
  
  if (options[:limit])
    limit = options[:limit]
    if (result_set.size > limit)
      result_set = result_set[0, limit]
    end
  end
  
  if (column = options[:column])
    if (!result_set.methods.include?(column))
      return []
    end
    begin
    #if there is a group by expression on a valid column
    #gather arrays of values
    if ((group = options[:group]) && result_set.method(options[:group]))
      #store an empty array under the key if there is no value
      groups = Hash.new {|groups, key| groups[key] = []}
      result_set.each do |object|
        groups[object.method(group).call] << object.method(column).call
      end
      return groups
    end
    rescue NameError => error
      Logger.warn 'Attempt to group by missing column:'
      Logger.warn 'Column \'' + options[:group].to_s + '\' does not exist on table \'' + klass.name + '\''
      raise error
    end
    
    #when called on a resultset a column method returns an array of that 
    #column's values
    return result_set.method(column).call()
  end

  result_set.map { |object| convert_to_instance(klass, object) }
end

#reload(obj, pk) ⇒ Object



125
126
127
128
129
# File 'lib/og/store/kirby.rb', line 125

def reload(obj, pk)
  raise 'Cannot reload unmanaged object' unless obj.saved?
  new_obj = load(pk, obj.class)
  #TODO: replace obj with new_obj
end

#rollbackObject

Rollback a transaction.



297
298
299
# File 'lib/og/store/kirby.rb', line 297

def rollback
  # nop, not supported?
end

#sql_update(sql) ⇒ Object



301
302
303
# File 'lib/og/store/kirby.rb', line 301

def sql_update(sql)
  # nop, not supported.
end

#startObject



285
286
287
# File 'lib/og/store/kirby.rb', line 285

def start
  # nop
end

#unjoin(obj1, obj2, table) ⇒ Object



324
325
326
327
328
329
330
331
# File 'lib/og/store/kirby.rb', line 324

def unjoin(obj1, obj2, table)
  first, second = join_object_ordering(obj1, obj2)
  
  @conn.get_table(table.to_sym).delete do |r|
    r.send(get_key(first)) == first.pk and
      r.send(get_key(second)) == second.pk
  end
end