gom 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +70 -0
- data/lib/gom/object.rb +4 -1
- data/lib/gom/object/builder.rb +47 -0
- data/lib/gom/object/cached_builder.rb +42 -0
- data/lib/gom/object/collection.rb +72 -0
- data/lib/gom/object/draft.rb +34 -0
- data/lib/gom/object/inspector.rb +29 -15
- data/lib/gom/spec/acceptance/adapter_with_stateful_storage.rb +74 -5
- data/lib/gom/spec/acceptance/read_only_adapter_with_stateless_storage.rb +20 -0
- data/lib/gom/storage.rb +6 -2
- data/lib/gom/storage/adapter.rb +11 -4
- data/lib/gom/storage/configuration.rb +30 -1
- data/lib/gom/storage/configuration/view.rb +20 -0
- data/lib/gom/storage/configuration/view/class.rb +28 -0
- data/lib/gom/storage/configuration/view/map_reduce.rb +29 -0
- data/lib/gom/storage/fetcher.rb +9 -34
- data/lib/gom/storage/saver.rb +5 -7
- data/spec/acceptance/object_spec.rb +1 -0
- data/spec/fake_adapter.rb +55 -10
- data/spec/lib/gom/object/builder_spec.rb +51 -0
- data/spec/lib/gom/object/cached_builder_spec.rb +43 -0
- data/spec/lib/gom/object/collection_spec.rb +158 -0
- data/spec/lib/gom/object/draft_spec.rb +59 -0
- data/spec/lib/gom/object/inspector_spec.rb +8 -10
- data/spec/lib/gom/storage/adapter_spec.rb +7 -33
- data/spec/lib/gom/storage/configuration/view/class_spec.rb +17 -0
- data/spec/lib/gom/storage/configuration/view/map_reduce_spec.rb +21 -0
- data/spec/lib/gom/storage/configuration_spec.rb +29 -0
- data/spec/lib/gom/storage/fetcher_spec.rb +7 -38
- data/spec/lib/gom/storage/remover_spec.rb +7 -9
- data/spec/lib/gom/storage/saver_spec.rb +17 -33
- data/spec/lib/gom/storage_spec.rb +33 -9
- data/spec/storage.configuration +10 -0
- metadata +22 -6
- data/lib/gom/object/injector.rb +0 -45
- data/spec/lib/gom/object/injector_spec.rb +0 -51
@@ -47,4 +47,24 @@ shared_examples_for "a read-only adapter connected to a stateless storage" do
|
|
47
47
|
|
48
48
|
end
|
49
49
|
|
50
|
+
describe "fetching a class collection" do
|
51
|
+
|
52
|
+
it "should return a collection" do
|
53
|
+
collection = GOM::Storage.collection :test_storage, :test_object_class_view
|
54
|
+
collection.should be_instance_of(GOM::Object::Collection)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return a collection that contains all object of class Object" do
|
58
|
+
collection = GOM::Storage.collection :test_storage, :test_object_class_view
|
59
|
+
collection.size > 0
|
60
|
+
collection.each do |object_proxy|
|
61
|
+
(
|
62
|
+
object_proxy.object.instance_variable_get(:@number) == 5 ||
|
63
|
+
object_proxy.object.instance_variable_get(:@test) == "test value"
|
64
|
+
).should be_true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
50
70
|
end
|
data/lib/gom/storage.rb
CHANGED
@@ -3,11 +3,11 @@ module GOM
|
|
3
3
|
|
4
4
|
module Storage
|
5
5
|
|
6
|
-
autoload :Configuration, File.join(File.dirname(__FILE__), "storage", "configuration")
|
7
6
|
autoload :Adapter, File.join(File.dirname(__FILE__), "storage", "adapter")
|
7
|
+
autoload :Configuration, File.join(File.dirname(__FILE__), "storage", "configuration")
|
8
8
|
autoload :Fetcher, File.join(File.dirname(__FILE__), "storage", "fetcher")
|
9
|
-
autoload :Saver, File.join(File.dirname(__FILE__), "storage", "saver")
|
10
9
|
autoload :Remover, File.join(File.dirname(__FILE__), "storage", "remover")
|
10
|
+
autoload :Saver, File.join(File.dirname(__FILE__), "storage", "saver")
|
11
11
|
|
12
12
|
# This error can be thrown by an adapter if it's doesn't support write operations
|
13
13
|
class ReadOnlyError < StandardError; end
|
@@ -30,6 +30,10 @@ module GOM
|
|
30
30
|
Remover.new(object_or_id).perform
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.collection(storage_name, *arguments)
|
34
|
+
Configuration[storage_name].adapter.collection *arguments
|
35
|
+
end
|
36
|
+
|
33
37
|
end
|
34
38
|
|
35
39
|
end
|
data/lib/gom/storage/adapter.rb
CHANGED
@@ -6,28 +6,35 @@ module GOM
|
|
6
6
|
# Base class for a storage adapter
|
7
7
|
class Adapter
|
8
8
|
|
9
|
+
# If a view could not be found, this error is raised.
|
10
|
+
class ViewNotFoundError < StandardError; end
|
11
|
+
|
9
12
|
attr_reader :configuration
|
10
13
|
|
11
14
|
def initialize(configuration)
|
12
15
|
@configuration = configuration
|
13
16
|
end
|
14
17
|
|
15
|
-
def setup
|
18
|
+
def setup(*arguments)
|
16
19
|
not_implemented "setup"
|
17
20
|
end
|
18
21
|
|
19
|
-
def fetch
|
22
|
+
def fetch(*arguments)
|
20
23
|
not_implemented "fetch"
|
21
24
|
end
|
22
25
|
|
23
|
-
def store
|
26
|
+
def store(*arguments)
|
24
27
|
not_implemented "store"
|
25
28
|
end
|
26
29
|
|
27
|
-
def remove
|
30
|
+
def remove(*arguments)
|
28
31
|
not_implemented "remove"
|
29
32
|
end
|
30
33
|
|
34
|
+
def collection(*arguments)
|
35
|
+
not_implemented "collection"
|
36
|
+
end
|
37
|
+
|
31
38
|
private
|
32
39
|
|
33
40
|
def not_implemented(method_name)
|
@@ -5,9 +5,11 @@ module GOM
|
|
5
5
|
|
6
6
|
module Storage
|
7
7
|
|
8
|
-
# Stores all information to configure a storage
|
8
|
+
# Stores all information to configure a storage.
|
9
9
|
class Configuration
|
10
10
|
|
11
|
+
autoload :View, File.join(File.dirname(__FILE__), "configuration", "view")
|
12
|
+
|
11
13
|
attr_reader :name
|
12
14
|
|
13
15
|
def initialize(name, hash)
|
@@ -35,6 +37,33 @@ module GOM
|
|
35
37
|
arguments.map{ |argument| self[argument] }
|
36
38
|
end
|
37
39
|
|
40
|
+
def views
|
41
|
+
@views ||= begin
|
42
|
+
result = { }
|
43
|
+
(self["views"] || { }).each do |name, hash|
|
44
|
+
result[name.to_sym] = self.class.view hash
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.view(hash)
|
53
|
+
type = hash["type"]
|
54
|
+
method_name = :"#{type}_view"
|
55
|
+
raise NotImplementedError, "the view type '#{type}' doesn't exists" unless self.respond_to?(method_name)
|
56
|
+
self.send method_name, hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.class_view(hash)
|
60
|
+
View::Class.new hash["class"]
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.map_reduce_view(hash)
|
64
|
+
View::MapReduce.new *hash.values_at("map", "reduce")
|
65
|
+
end
|
66
|
+
|
38
67
|
def self.read(file_name)
|
39
68
|
@configurations = { }
|
40
69
|
YAML::load_file(file_name).each do |name, values|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module GOM
|
3
|
+
|
4
|
+
module Storage
|
5
|
+
|
6
|
+
# Stores all information to configure a storage.
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
module View
|
10
|
+
|
11
|
+
autoload :Class, File.join(File.dirname(__FILE__), "view", "class")
|
12
|
+
autoload :MapReduce, File.join(File.dirname(__FILE__), "view", "map_reduce")
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module GOM
|
3
|
+
|
4
|
+
module Storage
|
5
|
+
|
6
|
+
# Stores all information to configure a storage.
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
module View
|
10
|
+
|
11
|
+
# Contains all the parameters for a class view.
|
12
|
+
class Class
|
13
|
+
|
14
|
+
attr_accessor :class_name
|
15
|
+
|
16
|
+
def initialize(class_name)
|
17
|
+
@class_name = class_name
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module GOM
|
3
|
+
|
4
|
+
module Storage
|
5
|
+
|
6
|
+
# Stores all information to configure a storage.
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
module View
|
10
|
+
|
11
|
+
# Contains all parameters for a map reduce view.
|
12
|
+
class MapReduce
|
13
|
+
|
14
|
+
attr_accessor :map
|
15
|
+
attr_accessor :reduce
|
16
|
+
|
17
|
+
def initialize(map, reduce)
|
18
|
+
@map, @reduce = map, reduce
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/gom/storage/fetcher.rb
CHANGED
@@ -13,55 +13,30 @@ module GOM
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def object
|
16
|
-
|
16
|
+
fetch_draft
|
17
|
+
return unless has_draft?
|
18
|
+
build_object
|
17
19
|
@object
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
21
23
|
|
22
|
-
def
|
23
|
-
|
24
|
-
return unless has_object_hash?
|
25
|
-
initialize_object
|
26
|
-
inject_object_hash
|
27
|
-
set_mapping
|
24
|
+
def fetch_draft
|
25
|
+
@draft = @id ? adapter.fetch(@id.object_id) : nil
|
28
26
|
end
|
29
27
|
|
30
|
-
def
|
31
|
-
|
28
|
+
def has_draft?
|
29
|
+
!!@draft
|
32
30
|
end
|
33
31
|
|
34
|
-
def
|
35
|
-
|
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
|
32
|
+
def build_object
|
33
|
+
@object = GOM::Object::CachedBuilder.new(@draft, @id).object
|
55
34
|
end
|
56
35
|
|
57
36
|
def adapter
|
58
37
|
@adapter ||= GOM::Storage::Configuration[@id.storage_name].adapter
|
59
38
|
end
|
60
39
|
|
61
|
-
def object_class
|
62
|
-
Object.const_get @object_hash[:class]
|
63
|
-
end
|
64
|
-
|
65
40
|
end
|
66
41
|
|
67
42
|
end
|
data/lib/gom/storage/saver.rb
CHANGED
@@ -17,7 +17,7 @@ module GOM
|
|
17
17
|
fetch_id
|
18
18
|
update_storage_name
|
19
19
|
inspect_object
|
20
|
-
|
20
|
+
store_draft
|
21
21
|
store_id
|
22
22
|
end
|
23
23
|
|
@@ -33,14 +33,12 @@ module GOM
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def inspect_object
|
36
|
-
|
37
|
-
|
38
|
-
@object_hash = inspector.object_hash
|
39
|
-
@object_hash[:id] = @id.object_id if @id
|
36
|
+
@draft = GOM::Object::Inspector.new(@object).draft
|
37
|
+
@draft.id = @id.object_id if @id
|
40
38
|
end
|
41
39
|
|
42
|
-
def
|
43
|
-
object_id = adapter.store @
|
40
|
+
def store_draft
|
41
|
+
object_id = adapter.store @draft
|
44
42
|
@id = GOM::Object::Id.new @storage_name.to_s, object_id
|
45
43
|
end
|
46
44
|
|
data/spec/fake_adapter.rb
CHANGED
@@ -1,35 +1,80 @@
|
|
1
1
|
|
2
2
|
class FakeAdapter < GOM::Storage::Adapter
|
3
3
|
|
4
|
-
|
4
|
+
class ClassCollectionFetcher
|
5
5
|
|
6
|
-
|
6
|
+
def initialize(store, storage_name, class_name)
|
7
|
+
@store, @storage_name, @class_name = store, storage_name, class_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def drafts
|
11
|
+
drafts = [ ]
|
12
|
+
@store.each do |object_id, draft|
|
13
|
+
drafts << draft if draft.class_name == @class_name
|
14
|
+
end
|
15
|
+
drafts
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class MapReduceCollectionFetcher
|
21
|
+
|
22
|
+
def initialize(store, storage_name, key, value)
|
23
|
+
@store, @storage_name, @key, @value = store, storage_name, key, value
|
24
|
+
end
|
25
|
+
|
26
|
+
def drafts
|
27
|
+
drafts = [ ]
|
28
|
+
@store.each do |object_id, draft|
|
29
|
+
drafts << draft if draft.properties[@key] == @value
|
30
|
+
end
|
31
|
+
drafts
|
32
|
+
end
|
7
33
|
|
8
34
|
end
|
9
35
|
|
36
|
+
def setup
|
37
|
+
@store = { }
|
38
|
+
end
|
39
|
+
|
10
40
|
def fetch(id)
|
11
|
-
|
41
|
+
@store[id]
|
12
42
|
end
|
13
43
|
|
14
|
-
def store(
|
44
|
+
def store(draft)
|
15
45
|
# store relations
|
16
|
-
|
46
|
+
draft.relations.each do |key, related_object_proxy|
|
17
47
|
GOM::Storage.store related_object_proxy.object, configuration.name
|
18
48
|
id = GOM::Object::Mapping.id_by_object related_object_proxy.object
|
19
|
-
|
49
|
+
draft.relations[key] = GOM::Object::Proxy.new id
|
20
50
|
end
|
21
51
|
|
22
52
|
# may generate an id
|
23
|
-
object_id =
|
53
|
+
object_id = draft.id || next_id
|
24
54
|
|
25
|
-
# store the
|
26
|
-
|
55
|
+
# store the draft
|
56
|
+
@store[object_id] = draft
|
27
57
|
|
28
58
|
object_id
|
29
59
|
end
|
30
60
|
|
31
61
|
def remove(id)
|
32
|
-
|
62
|
+
@store.delete id
|
63
|
+
end
|
64
|
+
|
65
|
+
def collection(view_name)
|
66
|
+
case view_name.to_sym
|
67
|
+
when :test_object_class_view
|
68
|
+
GOM::Object::Collection.new ClassCollectionFetcher.new(@store, configuration.name, "Object")
|
69
|
+
when :test_map_view
|
70
|
+
GOM::Object::Collection.new MapReduceCollectionFetcher.new(@store, configuration.name, :number, 11)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def next_id
|
77
|
+
"object_#{@store.size + 1}"
|
33
78
|
end
|
34
79
|
|
35
80
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe GOM::Object::Builder do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@related_object_proxy = Object.new
|
7
|
+
|
8
|
+
@draft = GOM::Object::Draft.new nil, "Object", { :test => "test value" }, { :related_object => @related_object_proxy }
|
9
|
+
@builder = described_class.new @draft
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "object" do
|
13
|
+
|
14
|
+
it "should initialize an object of the right class" do
|
15
|
+
object = @builder.object
|
16
|
+
object.class.should == Object
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not initialize an object if one is given" do
|
20
|
+
object = Object.new
|
21
|
+
@builder.object = object
|
22
|
+
@builder.object.should === object
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should set the properties" do
|
26
|
+
object = @builder.object
|
27
|
+
object.instance_variable_get(:@test).should == "test value"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set the relations" do
|
31
|
+
object = @builder.object
|
32
|
+
object.instance_variable_get(:@related_object).should == @related_object_proxy
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should work without a properties hash" do
|
36
|
+
@draft.properties = nil
|
37
|
+
lambda do
|
38
|
+
@builder.object
|
39
|
+
end.should_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should work without a relations hash" do
|
43
|
+
@draft.relations = nil
|
44
|
+
lambda do
|
45
|
+
@builder.object
|
46
|
+
end.should_not raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|