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.
- data/LICENSE +20 -0
- data/README.rdoc +111 -0
- data/Rakefile +48 -0
- data/lib/gom.rb +7 -0
- data/lib/gom/object.rb +23 -0
- data/lib/gom/object/id.rb +30 -0
- data/lib/gom/object/injector.rb +45 -0
- data/lib/gom/object/inspector.rb +55 -0
- data/lib/gom/object/mapping.rb +61 -0
- data/lib/gom/object/proxy.rb +44 -0
- data/lib/gom/spec.rb +4 -0
- data/lib/gom/spec/acceptance/adapter_with_stateful_storage.rb +111 -0
- data/lib/gom/spec/acceptance/read_only_adapter_with_stateless_storage.rb +50 -0
- data/lib/gom/storage.rb +35 -0
- data/lib/gom/storage/adapter.rb +51 -0
- data/lib/gom/storage/configuration.rb +65 -0
- data/lib/gom/storage/fetcher.rb +69 -0
- data/lib/gom/storage/remover.rb +47 -0
- data/lib/gom/storage/saver.rb +59 -0
- data/spec/acceptance/adapter_spec.rb +10 -0
- data/spec/acceptance/object_spec.rb +33 -0
- data/spec/fake_adapter.rb +37 -0
- data/spec/lib/gom/object/id_spec.rb +56 -0
- data/spec/lib/gom/object/injector_spec.rb +51 -0
- data/spec/lib/gom/object/inspector_spec.rb +30 -0
- data/spec/lib/gom/object/mapping_spec.rb +158 -0
- data/spec/lib/gom/object/proxy_spec.rb +91 -0
- data/spec/lib/gom/object_spec.rb +40 -0
- data/spec/lib/gom/storage/adapter_spec.rb +73 -0
- data/spec/lib/gom/storage/configuration_spec.rb +92 -0
- data/spec/lib/gom/storage/fetcher_spec.rb +89 -0
- data/spec/lib/gom/storage/remover_spec.rb +47 -0
- data/spec/lib/gom/storage/saver_spec.rb +86 -0
- data/spec/lib/gom/storage_spec.rb +106 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/storage.configuration +4 -0
- metadata +138 -0
data/lib/gom/spec.rb
ADDED
@@ -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
|
data/lib/gom/storage.rb
ADDED
@@ -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
|