gom 0.1.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 (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