gom 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +111 -0
  3. data/Rakefile +48 -0
  4. data/lib/gom.rb +7 -0
  5. data/lib/gom/object.rb +23 -0
  6. data/lib/gom/object/id.rb +30 -0
  7. data/lib/gom/object/injector.rb +45 -0
  8. data/lib/gom/object/inspector.rb +55 -0
  9. data/lib/gom/object/mapping.rb +61 -0
  10. data/lib/gom/object/proxy.rb +44 -0
  11. data/lib/gom/spec.rb +4 -0
  12. data/lib/gom/spec/acceptance/adapter_with_stateful_storage.rb +111 -0
  13. data/lib/gom/spec/acceptance/read_only_adapter_with_stateless_storage.rb +50 -0
  14. data/lib/gom/storage.rb +35 -0
  15. data/lib/gom/storage/adapter.rb +51 -0
  16. data/lib/gom/storage/configuration.rb +65 -0
  17. data/lib/gom/storage/fetcher.rb +69 -0
  18. data/lib/gom/storage/remover.rb +47 -0
  19. data/lib/gom/storage/saver.rb +59 -0
  20. data/spec/acceptance/adapter_spec.rb +10 -0
  21. data/spec/acceptance/object_spec.rb +33 -0
  22. data/spec/fake_adapter.rb +37 -0
  23. data/spec/lib/gom/object/id_spec.rb +56 -0
  24. data/spec/lib/gom/object/injector_spec.rb +51 -0
  25. data/spec/lib/gom/object/inspector_spec.rb +30 -0
  26. data/spec/lib/gom/object/mapping_spec.rb +158 -0
  27. data/spec/lib/gom/object/proxy_spec.rb +91 -0
  28. data/spec/lib/gom/object_spec.rb +40 -0
  29. data/spec/lib/gom/storage/adapter_spec.rb +73 -0
  30. data/spec/lib/gom/storage/configuration_spec.rb +92 -0
  31. data/spec/lib/gom/storage/fetcher_spec.rb +89 -0
  32. data/spec/lib/gom/storage/remover_spec.rb +47 -0
  33. data/spec/lib/gom/storage/saver_spec.rb +86 -0
  34. data/spec/lib/gom/storage_spec.rb +106 -0
  35. data/spec/spec_helper.rb +7 -0
  36. data/spec/storage.configuration +4 -0
  37. metadata +138 -0
data/lib/gom/spec.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rspec'
2
+
3
+ require File.join(File.dirname(__FILE__), "spec", "acceptance", "adapter_with_stateful_storage")
4
+ require File.join(File.dirname(__FILE__), "spec", "acceptance", "read_only_adapter_with_stateless_storage")
@@ -0,0 +1,111 @@
1
+
2
+ shared_examples_for "an adapter connected to a stateful storage" do
3
+
4
+ before :all do
5
+ GOM::Storage.setup
6
+ end
7
+
8
+ before :each do
9
+ @related_object = Object.new
10
+ @related_object.instance_variable_set :@number, 16
11
+
12
+ @object = Object.new
13
+ @object.instance_variable_set :@number, 11
14
+ @object.instance_variable_set :@related_object, GOM::Object.reference(@related_object)
15
+ end
16
+
17
+ describe "fetching an object" do
18
+
19
+ before :each do
20
+ GOM::Storage.store @object, :test_storage
21
+ @id = GOM::Object.id(@object)
22
+
23
+ @object = GOM::Storage.fetch @id
24
+ end
25
+
26
+ after :each do
27
+ GOM::Storage.remove @object
28
+ end
29
+
30
+ it "should return an object of the correct class" do
31
+ @object.class.should == Object
32
+ end
33
+
34
+ it "should set the object's instance variables" do
35
+ @object.instance_variable_get(:@number).should == 11
36
+ end
37
+
38
+ it "should assign an object id" do
39
+ GOM::Object.id(@object).should == @id
40
+ end
41
+
42
+ it "should also fetch the related object" do
43
+ related_object = @object.instance_variable_get :@related_object
44
+ related_object.should be_instance_of(GOM::Object::Proxy)
45
+ related_object.object.should == @related_object
46
+ related_object.object.instance_variable_get(:@number).should == 16
47
+ end
48
+
49
+ end
50
+
51
+ describe "storing an object" do
52
+
53
+ it "should store the object" do
54
+ GOM::Storage.store @object, :test_storage
55
+ object = GOM::Storage.fetch GOM::Object.id(@object)
56
+ object.should == @object
57
+ end
58
+
59
+ it "should use default storage if nothing specified" do
60
+ GOM::Storage.store @object
61
+ GOM::Object.id(@object).should =~ /^test_storage:/
62
+ end
63
+
64
+ it "should set the object's id" do
65
+ GOM::Storage.store @object, :test_storage
66
+ GOM::Object.id(@object).should =~ /^test_storage:.+$/
67
+ end
68
+
69
+ it "should store the related object" do
70
+ GOM::Storage.store @object, :test_storage
71
+ related_object = GOM::Storage.fetch GOM::Object.id(@related_object)
72
+ related_object.should == @related_object
73
+ related_object.instance_variable_get(:@number).should == 16
74
+ end
75
+
76
+ end
77
+
78
+ describe "removing an object" do
79
+
80
+ before :each do
81
+ GOM::Storage.store @object, :test_storage
82
+ @id = GOM::Object.id @object
83
+ end
84
+
85
+ after :each do
86
+ GOM::Storage.remove @related_object
87
+ end
88
+
89
+ it "should remove the object" do
90
+ GOM::Storage.remove @object
91
+ GOM::Storage.fetch(@id).should be_nil
92
+ end
93
+
94
+ it "should remove the object identified by the id" do
95
+ GOM::Storage.remove @id
96
+ GOM::Storage.fetch(@id).should be_nil
97
+ end
98
+
99
+ it "should remove the object's id" do
100
+ GOM::Storage.remove @object
101
+ GOM::Object.id(@object).should be_nil
102
+ end
103
+
104
+ it "should not remove the related object" do
105
+ GOM::Storage.remove @object
106
+ GOM::Object.id(@related_object).should_not be_nil
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,50 @@
1
+
2
+ shared_examples_for "a read-only adapter connected to a stateless storage" do
3
+
4
+ before :all do
5
+ GOM::Storage.setup
6
+ end
7
+
8
+ describe "fetching an object" do
9
+
10
+ it "should return the correct object" do
11
+ object = GOM::Storage.fetch "test_storage:object_1"
12
+ object.should be_instance_of(Object)
13
+ object.instance_variable_get(:@number).should == 5
14
+ GOM::Object.id(object).should == "test_storage:object_1"
15
+ end
16
+
17
+ it "should return proxy objects that fetches the related objects" do
18
+ object = GOM::Storage.fetch "test_storage:object_1"
19
+ related_object = object.instance_variable_get :@related_object
20
+ related_object.should be_instance_of(GOM::Object::Proxy)
21
+ related_object.object.instance_variable_get(:@test).should == "test value"
22
+ end
23
+
24
+ end
25
+
26
+ describe "storing an object" do
27
+
28
+ it "should raise a ReadOnlyError" do
29
+ lambda do
30
+ GOM::Storage.store Object.new, "test_storage"
31
+ end.should raise_error(GOM::Storage::ReadOnlyError)
32
+ end
33
+
34
+ end
35
+
36
+ describe "removing an object" do
37
+
38
+ before :each do
39
+ @object = GOM::Storage.fetch "test_storage:object_1"
40
+ end
41
+
42
+ it "should raise a ReadOnlyError" do
43
+ lambda do
44
+ GOM::Storage.remove @object
45
+ end.should raise_error(GOM::Storage::ReadOnlyError)
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,35 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ autoload :Configuration, File.join(File.dirname(__FILE__), "storage", "configuration")
7
+ autoload :Adapter, File.join(File.dirname(__FILE__), "storage", "adapter")
8
+ autoload :Fetcher, File.join(File.dirname(__FILE__), "storage", "fetcher")
9
+ autoload :Saver, File.join(File.dirname(__FILE__), "storage", "saver")
10
+ autoload :Remover, File.join(File.dirname(__FILE__), "storage", "remover")
11
+
12
+ # This error can be thrown by an adapter if it's doesn't support write operations
13
+ class ReadOnlyError < StandardError; end
14
+
15
+ def self.setup
16
+ Configuration.setup_all
17
+ end
18
+
19
+ def self.fetch(id_string)
20
+ id = id_string.is_a?(String) ? GOM::Object::Id.new(id_string) : nil
21
+ Fetcher.new(id).object
22
+ end
23
+
24
+ def self.store(object, storage_name = nil)
25
+ Saver.new(object, storage_name).perform
26
+ end
27
+
28
+ def self.remove(object_or_id)
29
+ object_or_id = GOM::Object::Id.new object_or_id if object_or_id.is_a?(String)
30
+ Remover.new(object_or_id).perform
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ # Base class for a storage adapter
7
+ class Adapter
8
+
9
+ attr_reader :configuration
10
+
11
+ def initialize(configuration)
12
+ @configuration = configuration
13
+ end
14
+
15
+ def setup
16
+ not_implemented "setup"
17
+ end
18
+
19
+ def fetch
20
+ not_implemented "fetch"
21
+ end
22
+
23
+ def store
24
+ not_implemented "store"
25
+ end
26
+
27
+ def remove
28
+ not_implemented "remove"
29
+ end
30
+
31
+ private
32
+
33
+ def not_implemented(method_name)
34
+ raise NotImplementedError, "The adapter has no #{method_name} method implemented"
35
+ end
36
+
37
+ def self.register(id, adapter_class)
38
+ @adapter_classes ||= { }
39
+ @adapter_classes[id.to_sym] = adapter_class
40
+ end
41
+
42
+ def self.[](id)
43
+ @adapter_classes ||= { }
44
+ @adapter_classes[id.to_sym]
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), "adapter")
2
+ require 'yaml'
3
+
4
+ module GOM
5
+
6
+ module Storage
7
+
8
+ # Stores all information to configure a storage
9
+ class Configuration
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(name, hash)
14
+ @name, @hash = name, { }
15
+ hash.each{ |key, value| @hash[key.to_sym] = value }
16
+ end
17
+
18
+ def setup
19
+ adapter.setup
20
+ end
21
+
22
+ def adapter
23
+ @adapter ||= adapter_class.new self
24
+ end
25
+
26
+ def adapter_class
27
+ @adapter_class ||= Adapter[@hash[:adapter]]
28
+ end
29
+
30
+ def [](key)
31
+ @hash[key.to_sym]
32
+ end
33
+
34
+ def values_at(*arguments)
35
+ arguments.map{ |argument| self[argument] }
36
+ end
37
+
38
+ def self.read(file_name)
39
+ @configurations = { }
40
+ YAML::load_file(file_name).each do |name, values|
41
+ @configurations[name.to_sym] = self.new name, values
42
+ end
43
+ end
44
+
45
+ def self.setup_all
46
+ @configurations.values.each do |configuration|
47
+ configuration.setup
48
+ end
49
+ end
50
+
51
+ def self.[](name)
52
+ @configurations ||= { }
53
+ @configurations[name.to_sym]
54
+ end
55
+
56
+ def self.default
57
+ @configurations ||= { }
58
+ @configurations.values.first || raise(StandardError, "No storage configuration loaded!")
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,69 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ # Fetches an object from the storage.
7
+ class Fetcher
8
+
9
+ attr_accessor :id
10
+
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def object
16
+ perform
17
+ @object
18
+ end
19
+
20
+ private
21
+
22
+ def perform
23
+ fetch_object_hash
24
+ return unless has_object_hash?
25
+ initialize_object
26
+ inject_object_hash
27
+ set_mapping
28
+ end
29
+
30
+ def fetch_object_hash
31
+ @object_hash = @id ? adapter.fetch(@id.object_id) : nil
32
+ end
33
+
34
+ def has_object_hash?
35
+ !!@object_hash
36
+ end
37
+
38
+ def initialize_object
39
+ @object = GOM::Object::Mapping.object_by_id @id
40
+ unless @object
41
+ klass = object_class
42
+ arity = [ klass.method(:new).arity, 0 ].max
43
+ @object = klass.new *([ nil ] * arity)
44
+ end
45
+ end
46
+
47
+ def inject_object_hash
48
+ injector = GOM::Object::Injector.new @object, @object_hash
49
+ injector.perform
50
+ @object = injector.object
51
+ end
52
+
53
+ def set_mapping
54
+ GOM::Object::Mapping.put @object, @id
55
+ end
56
+
57
+ def adapter
58
+ @adapter ||= GOM::Storage::Configuration[@id.storage_name].adapter
59
+ end
60
+
61
+ def object_class
62
+ Object.const_get @object_hash[:class]
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,47 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ # Removes an object from the storage.
7
+ class Remover
8
+
9
+ attr_reader :object
10
+ attr_reader :id
11
+
12
+ def initialize(object_or_id)
13
+ @object, @id = object_or_id.is_a?(GOM::Object::Id) ?
14
+ [ nil, object_or_id ] :
15
+ [ object_or_id, nil ]
16
+ end
17
+
18
+ def perform
19
+ check_mapping
20
+ remove_object
21
+ remove_mapping
22
+ end
23
+
24
+ private
25
+
26
+ def check_mapping
27
+ @id ||= GOM::Object::Mapping.id_by_object @object
28
+ raise ArgumentError, "No id existing for the given object!" unless @id
29
+ end
30
+
31
+ def remove_object
32
+ adapter.remove @id.object_id
33
+ end
34
+
35
+ def remove_mapping
36
+ GOM::Object::Mapping.remove_by_object @object
37
+ end
38
+
39
+ def adapter
40
+ @adapter ||= GOM::Storage::Configuration[@id.storage_name].adapter
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ # Stores an object to the storage.
7
+ class Saver
8
+
9
+ attr_reader :object
10
+ attr_reader :storage_name
11
+
12
+ def initialize(object, storage_name = nil)
13
+ @object, @storage_name = object, storage_name
14
+ end
15
+
16
+ def perform
17
+ fetch_id
18
+ update_storage_name
19
+ inspect_object
20
+ store_object_hash
21
+ store_id
22
+ end
23
+
24
+ private
25
+
26
+ def fetch_id
27
+ @id = GOM::Object::Mapping.id_by_object @object
28
+ end
29
+
30
+ def update_storage_name
31
+ @storage_name ||= @id.storage_name if @id
32
+ @storage_name ||= GOM::Storage::Configuration.default.name
33
+ end
34
+
35
+ def inspect_object
36
+ inspector = GOM::Object::Inspector.new @object
37
+ inspector.perform
38
+ @object_hash = inspector.object_hash
39
+ @object_hash[:id] = @id.object_id if @id
40
+ end
41
+
42
+ def store_object_hash
43
+ object_id = adapter.store @object_hash
44
+ @id = GOM::Object::Id.new @storage_name.to_s, object_id
45
+ end
46
+
47
+ def store_id
48
+ GOM::Object::Mapping.put @object, @id
49
+ end
50
+
51
+ def adapter
52
+ GOM::Storage::Configuration[@storage_name].adapter
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end