lib/knj/objects.rb in knjrbfw-0.0.21 vs lib/knj/objects.rb in knjrbfw-0.0.22
- old
+ new
@@ -11,14 +11,17 @@
@args[:col_id] = :id if !@args[:col_id]
@args[:class_pre] = "class_" if !@args[:class_pre]
@args[:module] = Kernel if !@args[:module]
@args[:cache] = :weak if !@args.key?(:cache)
@objects = {}
+ @locks = {}
@data = {}
@mutex_require = Mutex.new
- require "weakref" if @args[:cache] == :weak and !Kernel.const_defined?(:WeakRef)
+ if @args[:cache] == :weak
+ require "#{$knjpath}wref"
+ end
@events = Knj::Event_handler.new
@events.add_event(
:name => :no_html,
:connections_max => 1
@@ -55,12 +58,20 @@
end
end
end
def init_class(classname)
+ classname = classname.to_sym
return false if @objects.key?(classname)
- @objects[classname] = {}
+
+ if @args[:cache] == :weak
+ @objects[classname] = Knj::Wref_map.new
+ else
+ @objects[classname] = {}
+ end
+
+ @locks[classname] = Mutex.new
end
#Returns a cloned version of the @objects variable. Cloned because iteration on it may crash some of the other methods in Ruby 1.9+
def objects
objs_cloned = {}
@@ -70,34 +81,38 @@
end
return objs_cloned
end
+ #Returns the database-connection used by this instance of Objects.
def db
return @args[:db]
end
+ #Returns the total count of objects currently held by this instance.
def count_objects
count = 0
@objects.keys.each do |key|
count += @objects[key].length
end
return count
end
+ #This connects a block to an event. When the event is called the block will be executed.
def connect(args, &block)
raise "No object given." if !args["object"]
raise "No signals given." if !args.key?("signal") and !args.key?("signals")
args["block"] = block if block_given?
@callbacks[args["object"]] = {} if !@callbacks[args["object"]]
conn_id = @callbacks[args["object"]].length.to_s
@callbacks[args["object"]][conn_id] = args
end
+ #This method is used to call the connected callbacks for an event.
def call(args, &block)
- classstr = args["object"].class.to_s
+ classstr = args["object"].class.to_s.split("::").last
if @callbacks.key?(classstr)
@callbacks[classstr].clone.each do |callback_key, callback|
docall = false
@@ -130,14 +145,16 @@
end
end
def requireclass(classname, args = {})
classname = classname.to_sym
-
return false if @objects.key?(classname)
@mutex_require.synchronize do
+ #Maybe the classname got required meanwhile the synchronized wait - check again.
+ return false if @objects.key?(classname)
+
if (@args[:require] or !@args.key?(:require)) and (!args.key?(:require) or args[:require])
filename = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname.to_s.downcase}.rb"
filename_req = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname.to_s.downcase}"
raise "Class file could not be found: #{filename}." if !File.exists?(filename)
require filename_req
@@ -162,11 +179,11 @@
if (classob.respond_to?(:load_columns) or classob.respond_to?(:datarow_init)) and (!args.key?(:load) or args[:load])
self.load_class(classname, args)
end
- @objects[classname] = {}
+ self.init_class(classname)
end
end
#Loads a Datarow-class by calling various static methods.
def load_class(classname, args = {})
@@ -193,52 +210,48 @@
id = data[@args[:col_id].to_s].to_i
elsif
raise Knj::Errors::InvalidData, "Unknown data: '#{data.class.to_s}'."
end
- if @objects.key?(classname) and @objects[classname].key?(id)
+ if @objects.key?(classname)
case @args[:cache]
when :weak
- begin
- obj = @objects[classname][id].__getobj__
-
- if obj.is_a?(Knj::Datarow) and obj.respond_to?(:table) and obj.respond_to?(:id) and obj.table.to_sym == classname and obj.id.to_i == id
- return obj
- else
- #This actually happens sometimes... WTF!? - knj
- raise WeakRef::RefError
- end
- rescue WeakRef::RefError
- @objects[classname].delete(id)
- rescue NoMethodError => e
- #NoMethodError because the object might have been deleted from the cache, and __getobj__ then throws it.
- raise e if e.message != "undefined method `__getobj__' for nil:NilClass"
+ if obj = @objects[classname].get!(id) and obj.id.to_i == id
+ return obj
end
else
- return @objects[classname][id]
+ return @objects[classname][id] if @objects[classname].key?(id)
end
end
self.requireclass(classname) if !@objects.key?(classname)
- if @args[:datarow] or @args[:custom]
- obj = @args[:module].const_get(classname).new(Knj::Hash_methods.new(:ob => self, :data => data))
- else
- args = [data]
- args = args | @args[:extra_args] if @args[:extra_args]
- obj = @args[:module].const_get(classname).new(*args)
- end
-
- case @args[:cache]
- when :weak
- @objects[classname][id] = WeakRef.new(obj)
- when :none
+ @locks[classname].synchronize do
+ #Maybe the object got spawned while we waited for the lock? If so we shouldnt spawn another instance.
+ if obj = @objects[classname].get!(id) and obj.id.to_i == id
return obj
+ end
+
+ #Spawn object.
+ if @args[:datarow] or @args[:custom]
+ obj = @args[:module].const_get(classname).new(Knj::Hash_methods.new(:ob => self, :data => data))
else
- @objects[classname][id] = obj
+ args = [data]
+ args = args | @args[:extra_args] if @args[:extra_args]
+ obj = @args[:module].const_get(classname).new(*args)
+ end
+
+ #Save object in cache.
+ case @args[:cache]
+ when :none
+ return obj
+ else
+ @objects[classname][id] = obj
+ end
end
+ #Return spawned object.
return obj
end
def object_finalizer(id)
classname = @objects_idclass[id]
@@ -384,14 +397,12 @@
else
list_args = {}
end
if RUBY_VERSION[0..2] == 1.8 and Knj::Php.class_exists("Dictionary")
- print "Spawning dictionary.\n" if args[:debug]
list = Dictionary.new
else
- print "Spawning normal hash.\n" if args[:debug]
list = {}
end
if args[:addnew] or args[:add]
list["0"] = _("Add new")
@@ -401,43 +412,56 @@
list["0"] = _("All")
elsif args[:none]
list["0"] = _("None")
end
- print "Doing loop\n" if args[:debug]
self.list(classname, args[:list_args]) do |object|
- print "Object: #{object.id}\n" if args[:debug]
-
if object.respond_to?(:name)
list[object.id] = object.name
elsif object.respond_to?(:title)
list[object.id] = object.title
else
raise "Object of class '#{object.class.name}' doesnt support 'name' or 'title."
end
end
- print "Returning...\n" if args[:debug]
return list
end
#Returns a list of a specific object by running specific SQL against the database.
- def list_bysql(classname, sql, d = nil, &block)
+ def list_bysql(classname, sql, args = nil, &block)
classname = classname.to_sym
ret = [] if !block
- @args[:db].q(sql) do |d_obs|
+ qargs = nil
+
+ if args
+ args.each do |key, val|
+ case key
+ when :cloned_ubuf
+ qargs = {:cloned_ubuf => true}
+ else
+ raise "Invalid key: '#{key}'."
+ end
+ end
+ end
+
+ @args[:db].q(sql, qargs) do |d_obs|
if block
block.call(self.get(classname, d_obs))
else
ret << self.get(classname, d_obs)
end
end
- return ret if !block
+ if !block
+ return ret
+ else
+ return nil
+ end
end
- # Add a new object to the database and to the cache.
+ #Add a new object to the database and to the cache.
def add(classname, data = {})
classname = classname.to_sym
self.requireclass(classname)
if @args[:datarow]
@@ -575,28 +599,37 @@
end
classname = classname.to_sym
return false if !@objects.key?(classname)
- @objects[classname] = {}
+ @objects.delete(classname)
end
#Delete an object. Both from the database and from the cache.
def delete(object)
self.call("object" => object, "signal" => "delete_before")
self.unset(object)
obj_id = object.id
object.delete if object.respond_to?(:delete)
if @args[:datarow]
+ #If autodelete is set by 'has_many'-method, go through it and delete the various objects first.
+ object.class.autodelete_data.each do |adel_data|
+ self.list(adel_data[:classname], {adel_data[:colname].to_s => object.id}) do |obj_del|
+ self.delete(obj_del)
+ end
+ end
+
+ #If depend is set by 'has_many'-method, check if any objects exists and raise error if so.
object.class.depending_data.each do |dep_data|
- objs = self.list(dep_data[:classname], {dep_data[:colname].to_s => object.id, "limit" => 1})
- if !objs.empty?
- raise "Cannot delete <#{object.class.name}:#{object.id}> because <#{objs[0].class.name}:#{objs[0].id}> depends on it."
+ obj = self.get_by(dep_data[:classname], {dep_data[:colname].to_s => object.id})
+ if obj
+ raise "Cannot delete <#{object.class.name}:#{object.id}> because <#{obj.class.name}:#{obj.id}> depends on it."
end
end
+ #Delete any translations that has been set on the object by 'has_translation'-method.
if object.class.translations
_kas.trans_del(object)
end
@args[:db].delete(object.table, {:id => obj_id})
@@ -666,22 +699,11 @@
end
#Runs through all objects-weaklink-references and removes the weaklinks if the object has been recycled.
def clean_all_weak
@objects.keys.each do |classn|
- @objects[classn].keys.each do |object_id|
- object = @objects[classn][object_id]
-
- begin
- if !object or !object.weakref_alive?
- @objects[classn].delete(object_id)
- end
- rescue WeakRef::RefError
- #This happens if the object has been collected.
- @objects[classn].delete(object_id)
- end
- end
+ @objects[classn].clean
end
end
#Regenerates cache from ObjectSpace. Its pretty dangerous but can be used in envs where WeakRef is not supported (did someone say Rhodes?).
def clean_recover
@@ -705,6 +727,6 @@
end
end
end
end
-require "#{$knjpath}objects/objects_sqlhelper"
+require "#{$knjpath}objects/objects_sqlhelper"
\ No newline at end of file