knjrbfw 0.0.32 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/knj/objects.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Knj::Objects
2
- attr_reader :args, :events, :data
2
+ attr_reader :args, :events, :data, :ids_cache, :ids_cache_should
3
3
 
4
4
  def initialize(args)
5
5
  require "monitor"
@@ -8,7 +8,7 @@ class Knj::Objects
8
8
  require "#{$knjpath}hash_methods"
9
9
 
10
10
  @callbacks = {}
11
- @args = Knj::ArrayExt.hash_sym(args)
11
+ @args = args
12
12
  @args[:col_id] = :id if !@args[:col_id]
13
13
  @args[:class_pre] = "class_" if !@args[:class_pre]
14
14
  @args[:module] = Kernel if !@args[:module]
@@ -18,25 +18,14 @@ class Knj::Objects
18
18
  @data = {}
19
19
  @lock_require = Monitor.new
20
20
 
21
- require "#{$knjpath}wref" if @args[:cache] == :weak
21
+ require "wref" if @args[:cache] == :weak
22
22
 
23
+ #Set up various events.
23
24
  @events = Knj::Event_handler.new
24
- @events.add_event(
25
- :name => :no_html,
26
- :connections_max => 1
27
- )
28
- @events.add_event(
29
- :name => :no_date,
30
- :connections_max => 1
31
- )
32
- @events.add_event(
33
- :name => :missing_class,
34
- :connections_max => 1
35
- )
36
- @events.add_event(
37
- :name => :require_class,
38
- :connections_max => 1
39
- )
25
+ @events.add_event(:name => :no_html, :connections_max => 1)
26
+ @events.add_event(:name => :no_date, :connections_max => 1)
27
+ @events.add_event(:name => :missing_class, :connections_max => 1)
28
+ @events.add_event(:name => :require_class, :connections_max => 1)
40
29
 
41
30
  raise "No DB given." if !@args[:db] and !@args[:custom]
42
31
  raise "No class path given." if !@args[:class_path] and (@args[:require] or !@args.key?(:require))
@@ -60,6 +49,31 @@ class Knj::Objects
60
49
  self.load_class(load_class)
61
50
  end
62
51
  end
52
+
53
+ #Set up ID-caching.
54
+ @ids_cache_should = {}
55
+
56
+ if @args[:models]
57
+ @ids_cache = {}
58
+
59
+ @args[:models].each do |classname, classargs|
60
+ @ids_cache_should[classname] = true if classargs[:cache_ids]
61
+ self.cache_ids(classname)
62
+ end
63
+ end
64
+ end
65
+
66
+ #Caches all IDs for a specific classname.
67
+ def cache_ids(classname)
68
+ classname = classname.to_sym
69
+ return nil if !@ids_cache_should or !@ids_cache_should[classname]
70
+
71
+ newcache = {}
72
+ @args[:db].q("SELECT `#{@args[:col_id]}` FROM `#{classname}` ORDER BY `#{@args[:col_id]}`") do |data|
73
+ newcache[data[@args[:col_id]].to_i] = true
74
+ end
75
+
76
+ @ids_cache[classname] = newcache
63
77
  end
64
78
 
65
79
  def init_class(classname)
@@ -67,7 +81,7 @@ class Knj::Objects
67
81
  return false if @objects.key?(classname)
68
82
 
69
83
  if @args[:cache] == :weak
70
- @objects[classname] = Knj::Wref_map.new
84
+ @objects[classname] = Wref_map.new
71
85
  else
72
86
  @objects[classname] = {}
73
87
  end
@@ -116,6 +130,21 @@ class Knj::Objects
116
130
  @callbacks[args["object"]][conn_id] = args
117
131
  end
118
132
 
133
+ #Returns true if the given signal is connected to the given object.
134
+ def connected?(args)
135
+ raise "No object given." if !args["object"]
136
+ raise "No signal given." if !args.key?("signal")
137
+
138
+ if @callbacks.key?(args["object"])
139
+ @callbacks[args["object"]].clone.each do |ckey, callback|
140
+ return true if callback.key?("signal") and callback["signal"] == args["signal"]
141
+ return true if callback.key?("signals") and callback["signals"].index(args["signal"]) != nil
142
+ end
143
+ end
144
+
145
+ return false
146
+ end
147
+
119
148
  #This method is used to call the connected callbacks for an event.
120
149
  def call(args, &block)
121
150
  classstr = args["object"].class.to_s.split("::").last
@@ -222,8 +251,57 @@ class Knj::Objects
222
251
  classob.datarow_init(pass_arg) if classob.respond_to?(:datarow_init)
223
252
  end
224
253
 
254
+ #Returns the instance of classname, but only if it already exists.
255
+ def get_if_cached(classname, id)
256
+ classname = classname.to_sym
257
+ id = id.to_i
258
+
259
+ if wref_map = @objects[classname] and obj = wref_map.get!(id)
260
+ return obj
261
+ end
262
+
263
+ return nil
264
+ end
265
+
266
+ #Returns true if a row of the given classname and the ID exists. Will use ID-cache if set in arguments and spawned otherwise it will do an actual lookup.
267
+ #===Examples
268
+ # print "User 5 exists." if ob.exists?(:User, 5)
269
+ def exists?(classname, id)
270
+ #Make sure the given data are in the correct types.
271
+ classname = classname.to_sym
272
+ id = id.to_i
273
+
274
+ #Check if ID-cache is enabled for that classname. Avoid SQL-lookup by using that.
275
+ if @ids_cache_should.key?(classname)
276
+ if @ids_cache[classname].key?(id)
277
+ return true
278
+ else
279
+ return false
280
+ end
281
+ end
282
+
283
+ #If the object currently exists in cache, we dont have to do a lookup either.
284
+ return true if @objects.key?(classname) and obj = @objects[classname].get!(id) and !obj.deleted?
285
+
286
+ #Okay - no other options than to actually do a real lookup.
287
+ begin
288
+ table = @args[:module].const_get(classname).table
289
+ row = @args[:db].single(table, {@args[:col_id] => id})
290
+
291
+ if row
292
+ return true
293
+ else
294
+ return false
295
+ end
296
+ rescue Knj::Errors::NotFound
297
+ return false
298
+ end
299
+ end
300
+
225
301
  #Gets an object from the ID or the full data-hash in the database.
226
- def get(classname, data)
302
+ #===Examples
303
+ # inst = ob.get(:User, 5)
304
+ def get(classname, data, args = nil)
227
305
  classname = classname.to_sym
228
306
 
229
307
  if data.is_a?(Integer) or data.is_a?(String) or data.is_a?(Fixnum)
@@ -257,11 +335,11 @@ class Knj::Objects
257
335
 
258
336
  #Spawn object.
259
337
  if @args[:datarow] or @args[:custom]
260
- obj = @args[:module].const_get(classname).new(Knj::Hash_methods.new(:ob => self, :data => data))
338
+ obj = @args[:module].const_get(classname).new(data, args)
261
339
  else
262
- args = [data]
263
- args = args | @args[:extra_args] if @args[:extra_args]
264
- obj = @args[:module].const_get(classname).new(*args)
340
+ pass_args = [data]
341
+ pass_args = pass_args | @args[:extra_args] if @args[:extra_args]
342
+ obj = @args[:module].const_get(classname).new(*pass_args)
265
343
  end
266
344
 
267
345
  #Save object in cache.
@@ -489,36 +567,44 @@ class Knj::Objects
489
567
  end
490
568
 
491
569
  #Add a new object to the database and to the cache.
492
- def add(classname, data = {})
570
+ #===Examples
571
+ # obj = ob.add(:User, {:username => "User 1"})
572
+ def add(classname, data = {}, args = nil)
493
573
  raise "data-variable was not a hash: '#{data.class.name}'." if !data.is_a?(Hash)
494
574
  classname = classname.to_sym
495
575
  self.requireclass(classname)
496
576
 
497
577
  if @args[:datarow]
498
578
  classobj = @args[:module].const_get(classname)
499
- if classobj.respond_to?(:add)
500
- classobj.add(Knj::Hash_methods.new(
501
- :ob => self,
502
- :db => self.db,
503
- :data => data
504
- ))
505
- end
506
579
 
580
+ #Run the class 'add'-method to check various data.
581
+ classobj.add(Knj::Hash_methods.new(:ob => self, :db => @args[:db], :data => data)) if classobj.respond_to?(:add)
582
+
583
+ #Check if various required data is given. If not then raise an error telling about it.
507
584
  required_data = classobj.required_data
508
585
  required_data.each do |req_data|
509
- if !data.key?(req_data[:col])
510
- raise "No '#{req_data[:class]}' given by the data '#{req_data[:col]}'."
511
- end
512
-
513
- begin
514
- obj = self.get(req_data[:class], data[req_data[:col]])
515
- rescue Knj::Errors::NotFound
516
- raise "The '#{req_data[:class]}' by ID '#{data[req_data[:col]]}' could not be found with the data '#{req_data[:col]}'."
517
- end
586
+ raise "No '#{req_data[:class]}' given by the data '#{req_data[:col]}'." if !data.key?(req_data[:col])
587
+ raise "The '#{req_data[:class]}' by ID '#{data[req_data[:col]]}' could not be found with the data '#{req_data[:col]}'." if !self.exists?(req_data[:class], data[req_data[:col]])
588
+ end
589
+
590
+ #If 'skip_ret' is given, then the ID wont be looked up and the object wont be spawned. Be aware the connected events wont be executed either. In return it will go a lot faster.
591
+ if args and args[:skip_ret] and !@ids_cache_should.key?(classname)
592
+ ins_args = nil
593
+ else
594
+ ins_args = {:return_id => true}
518
595
  end
519
596
 
520
- ins_id = @args[:db].insert(classobj.table, data, {:return_id => true})
521
- retob = self.get(classname, ins_id)
597
+ #Insert and (maybe?) get ID.
598
+ ins_id = @args[:db].insert(classobj.table, data, ins_args).to_i
599
+
600
+ #Add ID to ID-cache if ID-cache is active for that classname.
601
+ @ids_cache[classname][ins_id] = true if ins_id != 0 and @ids_cache_should.key?(classname)
602
+
603
+ #Skip the rest if we are told not to return result.
604
+ return nil if args and args[:skip_ret]
605
+
606
+ #Spawn the object.
607
+ retob = self.get(classname, ins_id, {:skip_reload => true})
522
608
  elsif @args[:custom]
523
609
  classobj = @args[:module].const_get(classname)
524
610
  retob = classobj.add(Knj::Hash_methods.new(
@@ -532,14 +618,14 @@ class Knj::Objects
532
618
  end
533
619
 
534
620
  self.call("object" => retob, "signal" => "add")
535
- if retob.respond_to?(:add_after)
536
- retob.send(:add_after, {})
537
- end
621
+ retob.send(:add_after, {}) if retob.respond_to?(:add_after)
538
622
 
539
623
  return retob
540
624
  end
541
625
 
542
626
  #Adds several objects to the database at once. This is faster than adding every single object by itself, since this will do multi-inserts if supported by the database.
627
+ #===Examples
628
+ # ob.adds(:User, [{:username => "User 1"}, {:username => "User 2"})
543
629
  def adds(classname, datas)
544
630
  if !@args[:datarow]
545
631
  datas.each do |data|
@@ -559,6 +645,8 @@ class Knj::Objects
559
645
 
560
646
  db.insert_multi(classname, datas)
561
647
  end
648
+
649
+ self.cache_ids(classname)
562
650
  end
563
651
 
564
652
  #Calls a static method on a class. Passes the d-variable which contains the Objects-object, database-reference and more...
@@ -623,9 +711,13 @@ class Knj::Objects
623
711
  end
624
712
 
625
713
  #Delete an object. Both from the database and from the cache.
714
+ #===Examples
715
+ # user = ob.get(:User, 1)
716
+ # ob.delete(user)
626
717
  def delete(object)
627
718
  #Return false if the object has already been deleted.
628
719
  return false if object.deleted?
720
+ classname = object.class.classname.to_sym
629
721
 
630
722
  self.call("object" => object, "signal" => "delete_before")
631
723
  self.unset(object)
@@ -656,6 +748,7 @@ class Knj::Objects
656
748
  @args[:db].delete(object.table, {:id => obj_id})
657
749
  end
658
750
 
751
+ @ids_cache[classname].delete(obj_id.to_i) if @ids_cache_should.key?(classname)
659
752
  self.call("object" => object, "signal" => "delete")
660
753
  object.destroy
661
754
  end
@@ -667,87 +760,79 @@ class Knj::Objects
667
760
  self.delete(obj)
668
761
  end
669
762
  else
670
- arr_ids = []
671
- ids = []
672
- objs.each do |obj|
673
- next if obj.deleted?
674
- ids << obj.id
675
- if ids.length >= 1000
676
- arr_ids << ids
677
- ids = []
678
- end
679
-
680
- obj.delete if obj.respond_to?(:delete)
681
- end
763
+ tables = {}
682
764
 
683
- arr_ids << ids if ids.length > 0
684
- arr_ids.each do |ids|
685
- @args[:db].delete(objs[0].table, {:id => ids})
765
+ begin
766
+ objs.each do |obj|
767
+ next if obj.deleted?
768
+ tablen = obj.table
769
+
770
+ if !tables.key?(tablen)
771
+ tables[tablen] = []
772
+ end
773
+
774
+ tables[tablen] << obj.id
775
+ obj.delete if obj.respond_to?(:delete)
776
+
777
+ #Remove from ID-cache.
778
+ classname = obj.class.classname.to_sym
779
+ @ids_cache[classname].delete(obj.id.to_i) if @ids_cache_should.key?(classname)
780
+
781
+ #Unset any data on the object, so it seems deleted.
782
+ obj.destroy
783
+ end
784
+ ensure
785
+ #An exception may occur, and we should make sure, that objects that has gotten 'delete' called also are deleted from their tables.
786
+ tables.each do |table, ids|
787
+ ids.each_slice(1000) do |ids_slice|
788
+ @args[:db].delete(table, {:id => ids_slice})
789
+ end
790
+ end
686
791
  end
687
792
  end
688
793
  end
689
794
 
690
- # Try to clean up objects by unsetting everything, start the garbagecollector, get all the remaining objects via ObjectSpace and set them again. Some (if not all) should be cleaned up and our cache should still be safe... dirty but works.
795
+ #Try to clean up objects by unsetting everything, start the garbagecollector, get all the remaining objects via ObjectSpace and set them again. Some (if not all) should be cleaned up and our cache should still be safe... dirty but works.
691
796
  def clean(classn)
692
- return false if @args[:cache] == :weak or @args[:cache] == :none
693
-
694
797
  if classn.is_a?(Array)
695
798
  classn.each do |realclassn|
696
799
  self.clean(realclassn)
697
800
  end
801
+
802
+ return nil
803
+ end
804
+
805
+ if @args[:cache] == :weak
806
+ @objects[classn].clean
807
+ elsif @args[:cache] == :none
808
+ return false
698
809
  else
699
810
  return false if !@objects.key?(classn)
700
811
  @objects[classn] = {}
701
812
  GC.start
813
+
814
+ @objects.keys.each do |classn|
815
+ data = @objects[classn]
816
+ classobj = @args[:module].const_get(classn)
817
+ ObjectSpace.each_object(classobj) do |obj|
818
+ begin
819
+ data[obj.id.to_i] = obj
820
+ rescue => e
821
+ if e.message == "No data on object."
822
+ #Object has been unset - skip it.
823
+ next
824
+ end
825
+
826
+ raise e
827
+ end
828
+ end
829
+ end
702
830
  end
703
831
  end
704
832
 
705
833
  #Erases the whole cache and regenerates is from ObjectSpace if not running weak-link-caching. If running weaklink-caching then only removes the dead links.
706
834
  def clean_all
707
- return self.clean_all_weak if @args[:cache] == :weak
708
- return false if @args[:cache] == :none
709
-
710
- classnames = []
711
- @objects.keys.each do |classn|
712
- classnames << classn
713
- end
714
-
715
- classnames.each do |classn|
716
- @objects[classn] = {}
717
- end
718
-
719
- GC.start
720
- self.clean_recover
721
- end
722
-
723
- #Runs through all objects-weaklink-references and removes the weaklinks if the object has been recycled.
724
- def clean_all_weak
725
- @objects.keys.each do |classn|
726
- @objects[classn].clean
727
- end
728
- end
729
-
730
- #Regenerates cache from ObjectSpace. Its pretty dangerous but can be used in envs where WeakRef is not supported (did someone say Rhodes?).
731
- def clean_recover
732
- return false if @args[:cache] == :weak or @args[:cache] == :none
733
- return false if RUBY_ENGINE == "jruby" and !JRuby.objectspace
734
-
735
- @objects.keys.each do |classn|
736
- data = @objects[classn]
737
- classobj = @args[:module].const_get(classn)
738
- ObjectSpace.each_object(classobj) do |obj|
739
- begin
740
- data[obj.id.to_i] = obj
741
- rescue => e
742
- if e.message == "No data on object."
743
- #Object has been unset - skip it.
744
- next
745
- end
746
-
747
- raise e
748
- end
749
- end
750
- end
835
+ self.clean(@objects.keys)
751
836
  end
752
837
  end
753
838
 
@@ -3,6 +3,7 @@ class Knj::Threadhandler
3
3
 
4
4
  def initialize(args = {})
5
5
  require "#{$knjpath}errors"
6
+ require "tsafe"
6
7
 
7
8
  @args = args
8
9
  @objects = []
@@ -114,7 +115,7 @@ class Knj::Threadhandler
114
115
  else
115
116
  #No free objects, but we can spawn a new one and use that...
116
117
  newobj = @spawn_new_block.call
117
- @objects << Knj::Threadsafe::Synced_hash.new.merge(
118
+ @objects << Tsafe::MonHash.new.merge(
118
119
  :free => false,
119
120
  :object => newobj
120
121
  )
data/lib/knj/unix_proc.rb CHANGED
@@ -1,10 +1,10 @@
1
- require "#{$knjpath}wref"
1
+ require "wref"
2
2
 
3
3
  #This class handels various stuff regarding Unix-processes.
4
4
  class Knj::Unix_proc
5
5
  attr_reader :data
6
6
 
7
- PROCS = Knj::Wref_map.new
7
+ PROCS = Wref_map.new
8
8
  MUTEX = Mutex.new
9
9
 
10
10
  def self.spawn(data)
@@ -13,7 +13,7 @@ class Knj::Unix_proc
13
13
  begin
14
14
  proc_ele = PROCS[pid]
15
15
  proc_ele.update_data(data)
16
- rescue Knj::Wref::Recycled
16
+ rescue Wref::Recycled
17
17
  proc_ele = Knj::Unix_proc.new(data)
18
18
  PROCS[pid] = proc_ele
19
19
  end
data/spec/datet_spec.rb CHANGED
@@ -27,4 +27,47 @@ describe "Datet" do
27
27
  res = datet.ago_str
28
28
  raise "Expected '2 days ago' but got: '#{res}'." if res != "2 days ago"
29
29
  end
30
+
31
+ #From "knjrbfw_spec.rb".
32
+ it "should be able to parse various date formats." do
33
+ date = Knj::Datet.in("2011-07-09 00:00:00 UTC")
34
+ date = Knj::Datet.in("1985-06-17 01:00:00")
35
+ date = Knj::Datet.in("1985-06-17")
36
+ date = Knj::Datet.in("17/06 1985")
37
+
38
+ raise "Couldnt register type 1 nullstamp." if !Knj::Datet.is_nullstamp?("0000-00-00")
39
+ raise "Couldnt register type 2 nullstamp." if !Knj::Datet.is_nullstamp?("0000-00-00 00:00:00")
40
+ raise "Registered nullstamp on valid date." if Knj::Datet.is_nullstamp?("1985-06-17")
41
+ raise "Registered nullstamp on valid date." if Knj::Datet.is_nullstamp?("1985-06-17 10:30:00")
42
+
43
+ date = Knj::Datet.in("2011-07-09 13:05:04 +0200")
44
+ ltime = date.localtime_str
45
+
46
+ #if RUBY_VERSION.slice(0, 3) == "1.9"
47
+ # if ltime != date.time.localtime
48
+ # raise "Calculated localtime (#{ltime}) was not the same as the real Time-localtime (#{date.time.localtime})."
49
+ # end
50
+ #end
51
+
52
+ if ltime != "2011-07-09 13:05:04 +0200"
53
+ raise "Datet didnt return expected result: '#{ltime}'."
54
+ end
55
+ end
56
+
57
+ it "should be able to compare dates" do
58
+ date1 = Knj::Datet.in("17/06 1985")
59
+ date2 = Knj::Datet.in("18/06 1985")
60
+ date3 = Knj::Datet.in("17/06 1985")
61
+
62
+ raise "Date1 was wrongly higher than date2." if date1 > date2
63
+
64
+ if date2 > date1
65
+ #do nothing.
66
+ else
67
+ raise "Date2 was wrongly not higher than date1."
68
+ end
69
+
70
+ raise "Date1 was wrongly not the same as date3." if date1 != date3
71
+ raise "Date1 was the same as date2?" if date1 == date2
72
+ end
30
73
  end