ObjectModel 0.3.0

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.
Files changed (90) hide show
  1. data/README +849 -0
  2. data/lib/ObjectModel.res/config.yaml +5 -0
  3. data/lib/ObjectModel/AnEntity/BackReferences.rb +113 -0
  4. data/lib/ObjectModel/AnEntity/Bag.rb +129 -0
  5. data/lib/ObjectModel/AnEntity/BagCopy.rb +29 -0
  6. data/lib/ObjectModel/AnEntity/ChildrenBag.rb +10 -0
  7. data/lib/ObjectModel/AnEntity/EntityCopy.rb +38 -0
  8. data/lib/ObjectModel/AnEntity/EntityType.rb +408 -0
  9. data/lib/ObjectModel/AnEntity/ReferencesBag.rb +10 -0
  10. data/lib/ObjectModel/AnEntity/entity_cm.rb +108 -0
  11. data/lib/ObjectModel/Entity.rb +278 -0
  12. data/lib/ObjectModel/Errors/LoadError.rb +3 -0
  13. data/lib/ObjectModel/Errors/NoTransactionError.rb +3 -0
  14. data/lib/ObjectModel/Errors/NotFoundError.rb +2 -0
  15. data/lib/ObjectModel/Errors/OutdatedError.rb +8 -0
  16. data/lib/ObjectModel/Errors/ValidationError.rb +3 -0
  17. data/lib/ObjectModel/Indexes/HashIndex.rb +64 -0
  18. data/lib/ObjectModel/Indexes/IndexStorage.rb +64 -0
  19. data/lib/ObjectModel/Indexes/Manager.rb +72 -0
  20. data/lib/ObjectModel/Metadata.rb +34 -0
  21. data/lib/ObjectModel/Metadata.res/after_event_types.rb +25 -0
  22. data/lib/ObjectModel/Metadata.res/attribute_types_shortcuts.rb +10 -0
  23. data/lib/ObjectModel/Metadata.res/before_event_types.rb +25 -0
  24. data/lib/ObjectModel/Metadata.res/child_types_shortcuts.rb +4 -0
  25. data/lib/ObjectModel/Metadata.res/metadata_checks.rb +9 -0
  26. data/lib/ObjectModel/Metadata.res/reference_types_shortcuts.rb +4 -0
  27. data/lib/ObjectModel/Metadata/DSL.rb +11 -0
  28. data/lib/ObjectModel/Metadata/attribute.rb +56 -0
  29. data/lib/ObjectModel/Metadata/child.rb +50 -0
  30. data/lib/ObjectModel/Metadata/events.rb +109 -0
  31. data/lib/ObjectModel/Metadata/name.rb +20 -0
  32. data/lib/ObjectModel/Metadata/reference.rb +50 -0
  33. data/lib/ObjectModel/Metadata/require.rb +10 -0
  34. data/lib/ObjectModel/Metadata/validate.rb +66 -0
  35. data/lib/ObjectModel/Repository.rb +326 -0
  36. data/lib/ObjectModel/Repository/EventProcessor.rb +58 -0
  37. data/lib/ObjectModel/Repository/ObjectStorage.rb +102 -0
  38. data/lib/ObjectModel/Repository/StreamID.rb +20 -0
  39. data/lib/ObjectModel/Repository/StreamStorage.rb +121 -0
  40. data/lib/ObjectModel/Repository/SystemListener.rb +143 -0
  41. data/lib/ObjectModel/Repository/Transaction.rb +63 -0
  42. data/lib/ObjectModel/Repository/TransactionProcessor.rb +36 -0
  43. data/lib/ObjectModel/Tools/DefaultTransactionStrategy.rb +9 -0
  44. data/lib/ObjectModel/Tools/InMemoryCache.rb +9 -0
  45. data/lib/ObjectModel/Tools/NoCache.rb +18 -0
  46. data/lib/ObjectModel/Tools/OGLRUCache.rb +37 -0
  47. data/lib/ObjectModel/Types/BagType.rb +94 -0
  48. data/lib/ObjectModel/Types/BooleanType.rb +42 -0
  49. data/lib/ObjectModel/Types/ClassType.rb +43 -0
  50. data/lib/ObjectModel/Types/DataType.rb +44 -0
  51. data/lib/ObjectModel/Types/DateType.rb +43 -0
  52. data/lib/ObjectModel/Types/NumberType.rb +62 -0
  53. data/lib/ObjectModel/Types/ObjectType.rb +164 -0
  54. data/lib/ObjectModel/Types/ProcType.rb +43 -0
  55. data/lib/ObjectModel/Types/SingleType.rb +59 -0
  56. data/lib/ObjectModel/Types/StringType.rb +39 -0
  57. data/lib/ObjectModel/_require.rb +43 -0
  58. data/lib/ObjectModel/require.rb +1 -0
  59. data/rakefile +62 -0
  60. data/spec/ObjectModel/BasicSpec/BaseClass.rb +7 -0
  61. data/spec/ObjectModel/BasicSpec/BaseTypes.rb +11 -0
  62. data/spec/ObjectModel/BasicSpec/Descendant.rb +5 -0
  63. data/spec/ObjectModel/BasicSpec/EachTest.rb +14 -0
  64. data/spec/ObjectModel/BasicSpec/UpChild.rb +10 -0
  65. data/spec/ObjectModel/BasicSpec/UpParent.rb +11 -0
  66. data/spec/ObjectModel/ErrorsSpec/AfterCommitError.rb +9 -0
  67. data/spec/ObjectModel/ExtendedSpec/CustomInitialization.rb +11 -0
  68. data/spec/ObjectModel/ExtendedSpec/UpNotNil.rb +8 -0
  69. data/spec/ObjectModel/ExtendedSpec/UpParent.rb +11 -0
  70. data/spec/ObjectModel/ValidationSpec/BaseTypes.rb +11 -0
  71. data/spec/aspect_spec.rb +55 -0
  72. data/spec/back_references_spec.rb +161 -0
  73. data/spec/basic_spec.rb +162 -0
  74. data/spec/cascade_delete_spec.rb +168 -0
  75. data/spec/complex_events_spec.rb +134 -0
  76. data/spec/concurrency_spec.rb +186 -0
  77. data/spec/containment_spec.rb +146 -0
  78. data/spec/data_migration_spec.rb +51 -0
  79. data/spec/errors_spec.rb +65 -0
  80. data/spec/events_spec.rb +173 -0
  81. data/spec/extended_spec.rb +171 -0
  82. data/spec/indexing_spec.rb +56 -0
  83. data/spec/set_in_memory.rb +2 -0
  84. data/spec/set_no_cache.rb +2 -0
  85. data/spec/smoke_test_spec.rb +85 -0
  86. data/spec/stream_spec.rb +143 -0
  87. data/spec/stream_storage.rb +148 -0
  88. data/spec/timer.rb +5 -0
  89. data/spec/validation_spec.rb +87 -0
  90. metadata +186 -0
@@ -0,0 +1,58 @@
1
+ class EventProcessor
2
+ def initialize transaction
3
+ @transaction = transaction
4
+ @before_history = []
5
+ end
6
+
7
+ def fire_before entity, event, *params
8
+ entity.should! :be_a, Entity
9
+ event.should! :be_in, Metadata::BEFORE_EVENT_TYPES
10
+
11
+ l_event = :"before_#{event}"
12
+ @transaction.repository.entity_listeners.each{|l| l.respond_to l_event, entity, *params}
13
+ @transaction.system_listener.respond_to l_event, entity, *params
14
+
15
+ entity.meta.before.fire entity, event, *params
16
+ end
17
+
18
+ def fire_after entity, event, *params
19
+ entity.should! :be_a, Entity
20
+ event.should! :be_in, Metadata::AFTER_EVENT_TYPES
21
+
22
+ l_event = :"after_#{event}"
23
+ @transaction.repository.entity_listeners.each{|l| l.respond_to l_event, entity, *params}
24
+ @transaction.system_listener.respond_to l_event, entity, *params
25
+
26
+ entity.meta.after.fire entity, event, *params
27
+ end
28
+
29
+ def fire_before_commit
30
+ entities = []
31
+ @transaction.copies.each do |entity_id, c|
32
+ entities << @transaction.resolve(entity_id)
33
+ end
34
+
35
+ @transaction.repository.entity_listeners.each{|l| l.respond_to :before_commit, entities}
36
+ @transaction.system_listener.respond_to :before_commit, entities
37
+
38
+ entities.each{|e| e.meta.before.fire e, :commit}
39
+ end
40
+
41
+ def fire_after_commit
42
+ entities = []
43
+ @transaction.copies.each do |entity_id, c|
44
+ entity = if @transaction.deleted_entities.include? entity_id
45
+ @transaction.deleted_entities[entity_id]
46
+ else
47
+ @transaction.repository.by_id entity_id
48
+ end
49
+ entities << entity
50
+ end
51
+
52
+ # :after_commit
53
+ @transaction.repository.entity_listeners.each{|l| l.respond_to :after_commit, entities}
54
+ @transaction.system_listener.respond_to :after_commit, entities
55
+
56
+ entities.each{|e| e.meta.after.fire e, :commit}
57
+ end
58
+ end
@@ -0,0 +1,102 @@
1
+ class ObjectStorage
2
+ inherit Log
3
+ TYPES_TO_INITIALIZE = []
4
+
5
+ attr_reader :metadata
6
+
7
+ def initialize name, dir
8
+ folder = File.expand_path "#{dir}/#{name}" #.sub(/^\.\//, "")
9
+ Dir.mkdir folder unless File.exist? folder
10
+ @db = Sequel.connect("sqlite://#{folder}/#{name}.db")
11
+ @db.logger = log
12
+
13
+ unless @db.table_exists? :metadata
14
+ initialize_db
15
+ TYPES_TO_INITIALIZE.each{|type| type.initialize_storage @db}
16
+ end
17
+
18
+ @metadata ||= @db[:metadata]
19
+ @custom_metadata ||= @db[:custom_metadata]
20
+ @tables = {}
21
+ end
22
+
23
+ def [] type
24
+ @tables[type] ||= @db[type]
25
+ end
26
+
27
+ def generate generator_name
28
+ generator_name = generator_name.to_s
29
+ @db.transaction do
30
+ unless row = @metadata[:key => generator_name]
31
+ @metadata << {:key => generator_name, :value => "0"}
32
+ row = @metadata[:key => generator_name]
33
+ end
34
+ id = @metadata[:key => generator_name.to_s][:value].to_i
35
+ @metadata.filter(:key => generator_name).update(:value => (id + 1).to_s)
36
+ return id.to_s
37
+ end
38
+ end
39
+
40
+ def size
41
+ @db.transaction do
42
+ id = @metadata[:key => "size"][:value].to_i
43
+ end
44
+ end
45
+
46
+ def size= value
47
+ @db.transaction do
48
+ value.should! :be_a, Number
49
+ @metadata.filter(:key => "size").update(:value => value.to_s)
50
+ end
51
+ end
52
+
53
+ def close
54
+ @tables.clear
55
+ @db.disconnect
56
+ end
57
+
58
+ def transaction &b
59
+ @db.transaction &b
60
+ end
61
+
62
+ def print name = nil
63
+ TYPES_TO_INITIALIZE.each{|type| type.print_storage @db, name}
64
+ end
65
+
66
+ def get key
67
+ key.should! :be_a, String
68
+ row = @custom_metadata[:key => key]
69
+ return row ? row[:value] : nil
70
+ end
71
+
72
+ def put key, value
73
+ key.should! :be_a, String
74
+ value.should! :be_a, String
75
+
76
+ @custom_metadata.filter(:key => key).delete
77
+ @custom_metadata << {:key => key, :value => value}
78
+ end
79
+
80
+ protected
81
+ def initialize_db
82
+ @db.create_table :metadata do
83
+ column :key, :text
84
+ column :value, :text
85
+ primary_key :key
86
+ # index :key
87
+ end
88
+
89
+ @db.create_table :custom_metadata do
90
+ column :key, :text
91
+ column :value, :text
92
+ primary_key :key
93
+ # index :key
94
+ end
95
+
96
+ @metadata = @db[:metadata]
97
+ # @metadata << {:key => "entity_id", :value => "0"}
98
+ # @metadata << {:key => "name", :value => "0"}
99
+ # @metadata << {:key => "stream_id", :value => "0"}
100
+ @metadata << {:key => "size", :value => "0"}
101
+ end
102
+ end
@@ -0,0 +1,20 @@
1
+ class StreamID
2
+ attr_reader :sid
3
+ def initialize id = nil
4
+ @sid = id
5
+ end
6
+
7
+ def == other
8
+ return false unless other.respond_to? :sid
9
+ @sid == other.sid
10
+ end
11
+
12
+ def eql? other
13
+ return false unless self.class == other.class
14
+ @sid == other.sid
15
+ end
16
+
17
+ def hash
18
+ return @sid.hash
19
+ end
20
+ end
@@ -0,0 +1,121 @@
1
+ class StreamStorage
2
+ STREAMS = "streams"
3
+
4
+ def initialize name, basedir, buffer_size = 8192
5
+ @buffer_size = buffer_size
6
+ raise "Invalid directory '#{basedir}'!" unless File.exist? basedir
7
+ @path = File.join(basedir.gsub("\\", '/'), name.to_s)
8
+
9
+ Dir.mkdir @path unless File.exist? @path
10
+ Dir.mkdir File.join(@path, STREAMS) unless File.exist? File.join(@path, STREAMS)
11
+ end
12
+
13
+ def clear
14
+ FileUtils.rm Dir.glob(File.join(@path, STREAMS, "*.dat"))
15
+ end
16
+
17
+ def strip_id id
18
+ id.sid
19
+ end
20
+
21
+ def metadata_put id, metadata
22
+ id = strip_id(id)
23
+ metafile_name = File.join(@path, STREAMS, "#{id.to_s}.met")
24
+ File.open(metafile_name, "wb") do |datafile|
25
+ datafile.write YAML.dump(metadata)
26
+ end
27
+ end
28
+
29
+ def metadata_read id
30
+ id = strip_id(id)
31
+ begin
32
+ File.open(File.join(@path, STREAMS, "#{id.to_s}.met"), "rb") do |file|
33
+ return YAML.load(file.read)
34
+ end
35
+ rescue Exception => e
36
+ if e.message =~ /No such file or directory/
37
+ raise RuntimeError, "The Metadata with id '#{id}' not exist!", caller
38
+ else
39
+ raise e
40
+ end
41
+ end
42
+ end
43
+
44
+ def stream_put id, data = nil, &block
45
+ id = strip_id(id)
46
+ datafile_name = File.join(@path, STREAMS, "#{id.to_s}.dat")
47
+ File.open(datafile_name, "wb") do |datafile|
48
+ if block
49
+ block.call datafile
50
+ elsif data
51
+ datafile.write data
52
+ else
53
+ raise "Data is not supplied!"
54
+ end
55
+ end
56
+ end
57
+
58
+ def stream_put_each id, stream
59
+ stream_put id do |out|
60
+ while part = stream.read(@buffer_size)
61
+ out.write part
62
+ end
63
+ end
64
+ end
65
+
66
+ def stream_read id, &block
67
+ id = strip_id(id)
68
+ begin
69
+ File.open(File.join(@path, STREAMS, "#{id.to_s}.dat"), "rb") do |file|
70
+ if block
71
+ block.call file
72
+ else
73
+ return file.read
74
+ end
75
+ end
76
+ rescue Exception => e
77
+ if e.message =~ /No such file or directory/
78
+ raise RuntimeError, "The Stream with id '#{id}' has been deleted!", caller
79
+ else
80
+ raise e
81
+ end
82
+ end
83
+ end
84
+
85
+ def stream_read_each id, &block
86
+ stream_read id do |f|
87
+ while part = f.read(@buffer_size)
88
+ block.call part
89
+ end
90
+ end
91
+ end
92
+
93
+ def stream_size id
94
+ id = strip_id(id)
95
+ name = File.join(@path, STREAMS, "#{id.to_s}.dat")
96
+ raise RuntimeError, "The Stream with id '#{id}' has been deleted!", caller unless File.exist? name
97
+ File.size(name)
98
+ end
99
+
100
+ def size
101
+ Dir.glob(File.join(@path, STREAMS, "*.dat")).size
102
+ end
103
+
104
+ def delete id
105
+ id = strip_id(id)
106
+ name = File.join(@path, STREAMS, "#{id.to_s}.dat")
107
+ File.delete name if File.exist? name
108
+
109
+ name = File.join(@path, STREAMS, "#{id.to_s}.met")
110
+ File.delete name if File.exist? name
111
+ end
112
+
113
+ def list_of_ids
114
+ Dir.glob(File.join(@path, STREAMS, "*.dat")).collect{|name| File.basename(name, ".dat")}
115
+ end
116
+
117
+ def self.delete name, basedir
118
+ path = File.join(basedir, name.to_s)
119
+ FileUtils.rm_rf path if File.exist? path
120
+ end
121
+ end
@@ -0,0 +1,143 @@
1
+ class SystemListener
2
+ def initialize transaction
3
+ @transaction = transaction
4
+ @deleted_parents = {}
5
+ end
6
+
7
+ def before_attribute_update entity, name, new, old
8
+ name.should! :be_a, Symbol
9
+ AnEntity::EntityType.validate_attribute entity, name, new, old
10
+ end
11
+
12
+ def before_name_update entity, new, old
13
+ return if new == old
14
+ raise_without_self "name '#{new}' should be a String!", ObjectModel unless new.is_a? String
15
+ raise_without_self "name '#{new}' shouldn't be Emtpy!", ObjectModel if new.empty?
16
+ raise_without_self "name '#{new}' should not include '/' Symbol!", ObjectModel if new.include? '/'
17
+
18
+ check_name_uniquity entity, new
19
+ end
20
+
21
+ def after_new_parent entity, old_parent
22
+ check_name_uniquity entity, entity.name
23
+ end
24
+
25
+ def after_delete_parent entity, old_parent
26
+ check_name_uniquity entity, entity.name
27
+ end
28
+
29
+ def before_commit entities
30
+ entities.should! :be_a, Array
31
+ entities.each do |e|
32
+ AnEntity::EntityType.validate_entity e
33
+ end
34
+ end
35
+
36
+ def after_new entity
37
+ AnEntity::EntityType.custom_initialization entity
38
+ end
39
+
40
+ def before_new_child entity, name, child
41
+ raise_without_self "Child should be Entity or Nil (#{child})!", ObjectModel unless child.is_a?(Entity) or child == nil
42
+ raise_without_self "Forbiden to add self as Child!", ObjectModel if child == entity
43
+ raise_without_self "Forbiden to add the same Child twice!", ObjectModel if child.parent == entity
44
+
45
+ name.should! :be_a, Symbol
46
+
47
+ old_parent = child.parent
48
+ if old_parent != nil # :move
49
+ AnEntity::EntityType.delete_from_parent child, old_parent
50
+ @transaction.event_processor.fire_before child, :move, entity, old_parent
51
+ @transaction.copies[entity.entity_id].moved!
52
+ end
53
+
54
+ @transaction.event_processor.fire_before child, :new_parent, entity
55
+
56
+ child._parent = entity
57
+
58
+ @transaction.event_processor.fire_after child, :new_parent, old_parent
59
+
60
+ if old_parent != nil # :move
61
+ @transaction.event_processor.fire_after child, :move, entity, old_parent
62
+ end
63
+ end
64
+
65
+ def before_delete_child entity, name, child
66
+ child.should! :be_a, Entity
67
+ entity.should! :be_a, Entity
68
+ name.should! :be_a, Symbol
69
+
70
+ @transaction.event_processor.fire_before child, :delete_parent, entity
71
+ child._parent = nil
72
+ @transaction.event_processor.fire_after child, :delete_parent, entity
73
+ end
74
+
75
+ def before_delete entity
76
+ if entity.name_get == "child2"
77
+ copy = @transaction.copies[entity.entity_id]
78
+
79
+ parent = entity.parent
80
+ parent.should! :be_a, [Entity, NilClass]
81
+
82
+ @transaction.deleted_entities[entity.entity_id] = entity
83
+
84
+ AnEntity::EntityType.delete_all_references_to @transaction, entity
85
+ AnEntity::EntityType.delete_from_parent entity, parent if parent
86
+ AnEntity::EntityType.delete_all_children entity
87
+ return
88
+ end
89
+
90
+ copy = @transaction.copies[entity.entity_id]
91
+ copy.should_not! :deleted?
92
+
93
+ parent = entity.parent
94
+ parent.should! :be_a, [Entity, NilClass]
95
+
96
+ @transaction.deleted_entities[entity.entity_id] = entity
97
+
98
+ AnEntity::EntityType.delete_all_references_to @transaction, entity
99
+ AnEntity::EntityType.delete_from_parent entity, parent if parent
100
+ AnEntity::EntityType.delete_all_children entity
101
+ end
102
+
103
+ def before_delete_reference entity, name, reference
104
+ entity.should! :be_a, Entity
105
+ reference.should! :be_a, Entity
106
+ name.should! :be_a, Symbol
107
+
108
+ @transaction.event_processor.fire_before reference, :delete_referrer, entity
109
+
110
+ AnEntity::EntityType.delete_backreference @transaction, entity, reference
111
+
112
+ @transaction.event_processor.fire_after reference, :delete_referrer, entity
113
+ end
114
+
115
+ def before_new_reference entity, name, reference
116
+ entity.should! :be_a, Entity
117
+ reference.should! :be_a, Entity
118
+ name.should! :be_a, Symbol
119
+
120
+ @transaction.event_processor.fire_before reference, :new_referrer, entity
121
+
122
+ AnEntity::EntityType.new_backreference @transaction, entity, reference
123
+
124
+ @transaction.event_processor.fire_after reference, :new_referrer, entity
125
+ end
126
+
127
+ protected
128
+ def check_name_uniquity entity, name
129
+ # Uniquity In Transaction Scope
130
+ the_same = @transaction.new_entities.values.any? do |e|
131
+ next if e == entity
132
+ e.name == name and e.parent == entity.parent
133
+ end
134
+
135
+ # Uniquitey In Database
136
+ parent = entity.parent
137
+ parent_path = parent != nil ? parent.path + "/" : ""
138
+ absolute_path = "#{parent_path}#{name}"
139
+ the_same ||= @transaction.repository.include? absolute_path
140
+
141
+ raise_without_self "Not unique name '#{name}' (Parent '#{entity.parent}')!", ObjectModel if the_same
142
+ end
143
+ end