lib/og/store/kirby.rb in og-0.20.0 vs lib/og/store/kirby.rb in og-0.21.0
- old
+ new
@@ -1,10 +1,10 @@
begin
- require 'lib/og/store/kirby/kirbybase'
+ require 'lib/og/store/kirby/kirbybase'
rescue Object => ex
- Logger.error 'KirbyBase is not installed!'
- Logger.error ex
+ Logger.error 'KirbyBase is not installed!'
+ Logger.error ex
end
require 'og/store/sql'
module Og
@@ -14,267 +14,272 @@
# To read documentation about the methods, consult the
# documentation for SqlStore and Store.
class KirbyStore < SqlStore
- def self.db_dir(options)
- "#{options[:name]}_db"
- end
-
- def self.destroy(options)
- begin
- FileUtils.rm_rf(db_dir(options))
- super
- rescue Object
- Logger.info "Cannot drop '#{options[:name]}'!"
- end
- end
+ def self.db_dir(options)
+ "#{options[:name]}_db"
+ end
+
+ def self.destroy(options)
+ begin
+ FileUtils.rm_rf(db_dir(options))
+ super
+ rescue Object
+ Logger.info "Cannot drop '#{options[:name]}'!"
+ end
+ end
- def initialize(options)
- super
-
- if options[:embedded]
- name = self.class.db_dir(options)
- FileUtils.mkdir_p(name)
- @conn = KirbyBase.new(:local, nil, nil, name)
- else
- # TODO
- end
- end
+ def initialize(options)
+ super
+
+ if options[:embedded]
+ name = self.class.db_dir(options)
+ FileUtils.mkdir_p(name)
+ @conn = KirbyBase.new(:local, nil, nil, name)
+ else
+ # TODO
+ end
+ end
- def close
- super
- end
-
- def enchant(klass, manager)
- klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
- super
- end
+ def close
+ super
+ end
+
+ def enchant(klass, manager)
+ klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
+ super
+ end
- def query(sql)
- Logger.debug sql if $DBG
- return @conn.query(sql)
- rescue => ex
- handle_sql_exception(ex, sql)
- end
+ def query(sql)
+ Logger.debug sql if $DBG
+ return @conn.query(sql)
+ rescue => ex
+ handle_sql_exception(ex, sql)
+ end
- def exec(sql)
- Logger.debug sql if $DBG
- @conn.query(sql).close
- rescue => ex
- handle_sql_exception(ex, sql)
- end
+ def exec(sql)
+ Logger.debug sql if $DBG
+ @conn.query(sql).close
+ rescue => ex
+ handle_sql_exception(ex, sql)
+ end
- def start
- # nop
- end
-
- def commit
- # nop
- end
-
- def rollback
- # nop
- end
+ def start
+ # nop
+ end
+
+ def commit
+ # nop
+ end
+
+ def rollback
+ # nop
+ end
private
- def create_table(klass)
- fields = fields_for_class(klass)
+ def create_table(klass)
+ fields = fields_for_class(klass)
- table = @conn.create_table(klass::OGTABLE, *fields) { |obj| obj.encrypt = false }
-=begin
- # Create join tables if needed. Join tables are used in
- # 'many_to_many' relations.
-
- if klass.__meta and join_tables = klass.__meta[:join_tables]
- for join_table in join_tables
- begin
- @conn.query("CREATE TABLE #{join_table} (key1 integer NOT NULL, key2 integer NOT NULL)").close
- @conn.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").close
- @conn.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").close
- rescue Object => ex
- # gmosx: any idea how to better test this?
- if ex.to_s =~ /table .* already exists/i
- Logger.debug 'Join table already exists' if $DBG
- else
- raise
- end
- end
- end
- end
-=end
- end
+ table = @conn.create_table(klass::OGTABLE, *fields) { |obj| obj.encrypt = false }
- def drop_table(klass)
- @conn.drop_table(klass.table) if @conn.table_exists?(klass.table)
- end
+ # Create join tables if needed. Join tables are used in
+ # 'many_to_many' relations.
+
+ if klass.__meta and join_tables = klass.__meta[:join_tables]
+ for join_table in join_tables
+ begin
+ @conn.create_table(join_table[:table],
+ join_table[:first_key], :Integer,
+ join_table[:second_key], :Integer)
+ # KirbyBase doesn't support indices.
+ rescue RuntimeError => error
+ # Unfortunately, KirbyBase just throws RuntimeErrors
+ # with no extra information, so we just have to look
+ # for the error message it uses.
+ if error.message =~ /table #{join_table[:table]} already exists/i
+ Logger.debug 'Join table already exists' if $DBG
+ else
+ raise
+ end
+ end
+ end
+ end
+ end
- def fields_for_class(klass)
- fields = []
-
- klass.properties.each do |p|
- klass.index(p.symbol) if p.meta[:index]
-
- fields << p.symbol
-
- type = p.klass.name.intern
- type = :Integer if type == :Fixnum
-
- fields << type
- end
+ def drop_table(klass)
+ @conn.drop_table(klass.table) if @conn.table_exists?(klass.table)
+ end
- return fields
- end
+ def fields_for_class(klass)
+ fields = []
+
+ klass.properties.each do |p|
+ klass.index(p.symbol) if p.meta[:index]
+
+ fields << p.symbol
+
+ type = p.klass.name.intern
+ type = :Integer if type == :Fixnum
+
+ fields << type
+ end
- def create_field_map(klass)
- map = {}
- fields = @conn.get_table(klass.table).field_names
+ return fields
+ end
- fields.size.times do |i|
- map[fields[i]] = i
- end
-
- return map
- end
+ def create_field_map(klass)
+ map = {}
+ fields = @conn.get_table(klass.table).field_names
- # Return an sql string evaluator for the property.
- # No need to optimize this, used only to precalculate code.
- # YAML is used to store general Ruby objects to be more
- # portable.
- #--
- # FIXME: add extra handling for float.
- #++
-
- def write_prop(p)
- if p.klass.ancestors.include?(Integer)
- return "@#{p.symbol} || nil"
- elsif p.klass.ancestors.include?(Float)
- return "@#{p.symbol} || nil"
- elsif p.klass.ancestors.include?(String)
- return %|@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : nil|
- elsif p.klass.ancestors.include?(Time)
- return %|@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : nil|
- elsif p.klass.ancestors.include?(Date)
- return %|@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : nil|
- elsif p.klass.ancestors.include?(TrueClass)
- return "@#{p.symbol} ? \"'t'\" : nil"
- else
- # gmosx: keep the '' for nil symbols.
- return %|@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"|
- end
- end
+ fields.size.times do |i|
+ map[fields[i]] = i
+ end
+
+ return map
+ end
- # Return an evaluator for reading the property.
- # No need to optimize this, used only to precalculate code.
-
- def read_prop(p, col)
- if p.klass.ancestors.include?(Integer)
- return "#{self.class}.parse_int(res[#{col} + offset])"
- elsif p.klass.ancestors.include?(Float)
- return "#{self.class}.parse_float(res[#{col} + offset])"
- elsif p.klass.ancestors.include?(String)
- return "res[#{col} + offset]"
- elsif p.klass.ancestors.include?(Time)
- return "#{self.class}.parse_timestamp(res[#{col} + offset])"
- elsif p.klass.ancestors.include?(Date)
- return "#{self.class}.parse_date(res[#{col} + offset])"
- elsif p.klass.ancestors.include?(TrueClass)
- return "('0' != res[#{col} + offset])"
- else
- return "YAML::load(res[#{col} + offset])"
- end
- end
+ # Return an sql string evaluator for the property.
+ # No need to optimize this, used only to precalculate code.
+ # YAML is used to store general Ruby objects to be more
+ # portable.
+ #--
+ # FIXME: add extra handling for float.
+ #++
+
+ def write_prop(p)
+ if p.klass.ancestors.include?(Integer)
+ return "@#{p.symbol} || nil"
+ elsif p.klass.ancestors.include?(Float)
+ return "@#{p.symbol} || nil"
+ elsif p.klass.ancestors.include?(String)
+ return %|@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : nil|
+ elsif p.klass.ancestors.include?(Time)
+ return %|@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : nil|
+ elsif p.klass.ancestors.include?(Date)
+ return %|@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : nil|
+ elsif p.klass.ancestors.include?(TrueClass)
+ return "@#{p.symbol} ? \"'t'\" : nil"
+ else
+ # gmosx: keep the '' for nil symbols.
+ return %|@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"|
+ end
+ end
- # :section: Lifecycle method compilers.
-
- # Compile the og_update method for the class.
+ # Return an evaluator for reading the property.
+ # No need to optimize this, used only to precalculate code.
+
+ def read_prop(p, col)
+ if p.klass.ancestors.include?(Integer)
+ return "#{self.class}.parse_int(res[#{col} + offset])"
+ elsif p.klass.ancestors.include?(Float)
+ return "#{self.class}.parse_float(res[#{col} + offset])"
+ elsif p.klass.ancestors.include?(String)
+ return "res[#{col} + offset]"
+ elsif p.klass.ancestors.include?(Time)
+ return "#{self.class}.parse_timestamp(res[#{col} + offset])"
+ elsif p.klass.ancestors.include?(Date)
+ return "#{self.class}.parse_date(res[#{col} + offset])"
+ elsif p.klass.ancestors.include?(TrueClass)
+ return "('0' != res[#{col} + offset])"
+ else
+ return "YAML::load(res[#{col} + offset])"
+ end
+ end
- def eval_og_insert(klass)
- pk = klass.pk_symbol
- props = klass.properties
-
- data = props.collect {|p| ":#{p.symbol} => #{write_prop(p)}"}.join(', ')
-# data.gsub!(/#|\{|\}/, '')
+ # :section: Lifecycle method compilers.
+
+ # Compile the og_update method for the class.
- klass.module_eval %{
- def og_insert(store)
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
- store.conn.get_table('#{klass.table}').insert(#{data})
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
- end
- }
- end
+ def eval_og_insert(klass)
+ pk = klass.pk_symbol
+ props = klass.properties
+
+ data = props.collect {|p| ":#{p.symbol} => #{write_prop(p)}"}.join(', ')
+# data.gsub!(/#|\{|\}/, '')
- # Compile the og_update method for the class.
+ klass.module_eval %{
+ def og_insert(store)
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
+ store.conn.get_table('#{klass.table}').insert(#{data})
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
+ end
+ }
+ end
- def eval_og_update(klass)
- pk = klass.pk_symbol
- props = klass.properties.reject { |p| pk == p.symbol }
-
- updates = props.collect { |p|
- "#{p.symbol}=#{write_prop(p)}"
- }
+ # Compile the og_update method for the class.
- sql = "UPDATE #{klass::OGTABLE} SET #{updates.join(', ')} WHERE #{pk}=#\{@#{pk}\}"
+ def eval_og_update(klass)
+ pk = klass.pk_symbol
+ props = klass.properties.reject { |p| pk == p.symbol }
+
+ updates = props.collect { |p|
+ "#{p.symbol}=#{write_prop(p)}"
+ }
- klass.module_eval %{
- def og_update(store)
- #{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
- store.exec "#{sql}"
- #{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
- end
- }
- end
+ sql = "UPDATE #{klass::OGTABLE} SET #{updates.join(', ')} WHERE #{pk}=#\{@#{pk}\}"
- # Compile the og_read method for the class. This method is
- # used to read (deserialize) the given class from the store.
- # In order to allow for changing field/attribute orders a
- # field mapping hash is used.
-
- def eval_og_read(klass)
- code = []
- props = klass.properties
- field_map = create_field_map(klass)
+ klass.module_eval %{
+ def og_update(store)
+ #{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
+ store.exec "#{sql}"
+ #{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
+ end
+ }
+ end
- props.each do |p|
- if col = field_map[p.symbol]
- code << "@#{p.symbol} = #{read_prop(p, col)}"
- end
- end
-
- code = code.join('; ')
+ # Compile the og_read method for the class. This method is
+ # used to read (deserialize) the given class from the store.
+ # In order to allow for changing field/attribute orders a
+ # field mapping hash is used.
+
+ def eval_og_read(klass)
+ code = []
+ props = klass.properties
+ field_map = create_field_map(klass)
- klass.module_eval %{
- def og_read(res, row = 0, offset = 0)
- #{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
- #{code}
- #{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
- end
- }
- end
+ props.each do |p|
+ if col = field_map[p.symbol]
+ code << "@#{p.symbol} = #{read_prop(p, col)}"
+ end
+ end
+
+ code = code.join('; ')
- #--
- # FIXME: is pk needed as parameter?
- #++
-
- def eval_og_delete(klass)
- klass.module_eval %{
- def og_delete(store, pk, cascade = true)
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
- pk ||= @#{klass.pk_symbol}
- transaction do |tx|
- tx.exec "DELETE FROM #{klass.table} WHERE #{klass.pk_symbol}=\#{pk}"
- if cascade and #{klass}.__meta[:descendants]
- #{klass}.__meta[:descendants].each do |dclass, foreign_key|
- tx.exec "DELETE FROM \#{dclass::OGTABLE} WHERE \#{foreign_key}=\#{pk}"
- end
- end
- end
- #{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
- end
- }
- end
+ klass.module_eval %{
+ def og_read(res, row = 0, offset = 0)
+ #{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
+ #{code}
+ #{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
+ end
+ }
+ end
+ #--
+ # FIXME: is pk needed as parameter?
+ #++
+
+ def eval_og_delete(klass)
+ klass.module_eval %{
+ def og_delete(store, pk, cascade = true)
+ #{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
+ pk ||= @#{klass.pk_symbol}
+ transaction do |tx|
+ tx.exec "DELETE FROM #{klass.table} WHERE #{klass.pk_symbol}=\#{pk}"
+ if cascade and #{klass}.__meta[:descendants]
+ #{klass}.__meta[:descendants].each do |dclass, foreign_key|
+ tx.exec "DELETE FROM \#{dclass::OGTABLE} WHERE \#{foreign_key}=\#{pk}"
+ end
+ end
+ end
+ #{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
+ end
+ }
+ end
+
end
end
+
+# * George Moschovitis <gm@navel.gr>
+# * Ysabel <deb@isabel.org>