Skip to content

Commit c391a3b

Browse files
committed
[STORE] Implemented the gateway pattern for the repository integration
To provide flexibility to the end-user, the integration of the "Repository" module has been refactored to proxy all repository methods via a gateway. This allows users to set up the repository in a custom class, with class methods, in a DSL-like fashion: class NoteRepository include Elasticsearch::Persistence::Repository klass Note index :my_notes mapping do indexes :title, analyzer: 'snowball' end client.transport.logger = Logger.new(STDERR) gateway do def serialize(document) super.merge(special: 'stuff') end end end The bundled Repository class can be configured via a block passed to the initializer: repository = Elasticsearch::Persistence::Repository.new do klass Note index :my_notes mapping do indexes :title, analyzer: 'snowball' end client.transport.logger = Logger.new(STDERR) end The repository methods can be accessed via the class or instance methods: NoteRepository.klass Note repository.klass Note
1 parent e4e6ff2 commit c391a3b

File tree

5 files changed

+176
-28
lines changed

5 files changed

+176
-28
lines changed

elasticsearch-persistence/lib/elasticsearch/persistence.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
require 'elasticsearch/persistence/repository/store'
1414
require 'elasticsearch/persistence/repository/find'
1515
require 'elasticsearch/persistence/repository/search'
16-
require 'elasticsearch/persistence/repository'
17-
1816
require 'elasticsearch/persistence/repository/class'
17+
require 'elasticsearch/persistence/repository'
1918

2019
module Elasticsearch
2120
module Persistence

elasticsearch-persistence/lib/elasticsearch/persistence/repository.rb

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
module Elasticsearch
22
module Persistence
3+
module GatewayDelegation
4+
def method_missing(method_name, *arguments, &block)
5+
gateway.respond_to?(method_name) ? gateway.__send__(method_name, *arguments, &block) : super
6+
end
7+
8+
def respond_to?(method_name, include_private=false)
9+
gateway.respond_to?(method_name) || super
10+
end
11+
end
312

413
module Repository
5-
include Elasticsearch::Persistence::Client
6-
include Elasticsearch::Persistence::Repository::Naming
7-
include Elasticsearch::Persistence::Repository::Serialize
8-
include Elasticsearch::Persistence::Repository::Store
9-
include Elasticsearch::Persistence::Repository::Find
10-
include Elasticsearch::Persistence::Repository::Search
14+
def self.included(base)
15+
gateway = Elasticsearch::Persistence::Repository::Class.new
16+
17+
base.class_eval do
18+
define_method :gateway do
19+
@gateway ||= gateway
20+
end
21+
22+
include GatewayDelegation
23+
end
24+
25+
(class << base; self; end).class_eval do
26+
define_method :gateway do |&block|
27+
@gateway ||= gateway
28+
@gateway.instance_eval(&block) if block
29+
@gateway
30+
end
1131

12-
include Elasticsearch::Model::Indexing::ClassMethods
32+
include GatewayDelegation
33+
end
34+
end
1335

1436
def new(options={}, &block)
1537
Elasticsearch::Persistence::Repository::Class.new options, &block

elasticsearch-persistence/lib/elasticsearch/persistence/repository/class.rb

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ module Persistence
33
module Repository
44

55
class Class
6-
include Elasticsearch::Persistence::Repository
6+
include Elasticsearch::Persistence::Client
7+
include Elasticsearch::Persistence::Repository::Naming
8+
include Elasticsearch::Persistence::Repository::Serialize
9+
include Elasticsearch::Persistence::Repository::Store
10+
include Elasticsearch::Persistence::Repository::Find
11+
include Elasticsearch::Persistence::Repository::Search
12+
13+
include Elasticsearch::Model::Indexing::ClassMethods
714

815
attr_reader :options
916

elasticsearch-persistence/test/unit/repository_class_test.rb

+32-18
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,43 @@
33
class Elasticsearch::Persistence::RepositoryClassTest < Test::Unit::TestCase
44
context "The default repository class" do
55

6-
should "be created from the module" do
7-
repository = Elasticsearch::Persistence::Repository.new
8-
assert_instance_of Elasticsearch::Persistence::Repository::Class, repository
9-
end
6+
context "when initialized" do
7+
should "be created from the module" do
8+
repository = Elasticsearch::Persistence::Repository.new
9+
assert_instance_of Elasticsearch::Persistence::Repository::Class, repository
10+
end
1011

11-
should "store and access the options" do
12-
repository = Elasticsearch::Persistence::Repository::Class.new foo: 'bar'
13-
assert_equal 'bar', repository.options[:foo]
14-
end
12+
should "store and access the options" do
13+
repository = Elasticsearch::Persistence::Repository::Class.new foo: 'bar'
14+
assert_equal 'bar', repository.options[:foo]
15+
end
1516

16-
should "instance eval a passed block" do
17-
$foo = 100
18-
repository = Elasticsearch::Persistence::Repository::Class.new() { $foo += 1 }
19-
assert_equal 101, $foo
17+
should "instance eval a passed block" do
18+
$foo = 100
19+
repository = Elasticsearch::Persistence::Repository::Class.new() { $foo += 1 }
20+
assert_equal 101, $foo
21+
end
22+
23+
should "call a passed block with self" do
24+
foo = 100
25+
repository = Elasticsearch::Persistence::Repository::Class.new do |r|
26+
assert_instance_of Elasticsearch::Persistence::Repository::Class, r
27+
foo += 1
28+
end
29+
assert_equal 101, foo
30+
end
2031
end
2132

22-
should "call a passed block with self" do
23-
foo = 100
24-
repository = Elasticsearch::Persistence::Repository::Class.new do |r|
25-
assert_instance_of Elasticsearch::Persistence::Repository::Class, r
26-
foo += 1
33+
should "include the repository methods" do
34+
repository = Elasticsearch::Persistence::Repository::Class.new
35+
36+
%w( index_name document_type klass
37+
mappings settings client client=
38+
create_index! delete_index! refresh_index!
39+
save delete serialize deserialize
40+
exists? find search ).each do |method|
41+
assert_respond_to repository, method
2742
end
28-
assert_equal 101, foo
2943
end
3044

3145
end

elasticsearch-persistence/test/unit/repository_module_test.rb

+106
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,111 @@
22

33
class Elasticsearch::Persistence::RepositoryModuleTest < Test::Unit::TestCase
44
context "The repository module" do
5+
6+
class DummyModel
7+
def initialize(attributes={})
8+
@attributes = attributes
9+
end
10+
11+
def to_hash
12+
@attributes
13+
end
14+
15+
def inspect
16+
"<Note #{@attributes.inspect}>"
17+
end
18+
end
19+
20+
setup do
21+
class DummyRepository
22+
include Elasticsearch::Persistence::Repository
23+
end
24+
end
25+
26+
teardown do
27+
Elasticsearch::Persistence::RepositoryModuleTest.__send__ :remove_const, :DummyRepository
28+
end
29+
30+
context "when included" do
31+
should "set up the gateway for the class and instance" do
32+
assert_respond_to DummyRepository, :gateway
33+
assert_respond_to DummyRepository.new, :gateway
34+
35+
assert_instance_of Elasticsearch::Persistence::Repository::Class, DummyRepository.gateway
36+
assert_instance_of Elasticsearch::Persistence::Repository::Class, DummyRepository.new.gateway
37+
end
38+
39+
should "proxy repository methods from the class to the gateway" do
40+
class DummyRepository
41+
include Elasticsearch::Persistence::Repository
42+
43+
index :foobar
44+
klass DummyModel
45+
type :my_dummy_model
46+
mapping { indexes :title, analyzer: 'snowball' }
47+
end
48+
49+
repository = DummyRepository.new
50+
51+
assert_equal :foobar, DummyRepository.index
52+
assert_equal DummyModel, DummyRepository.klass
53+
assert_equal :my_dummy_model, DummyRepository.type
54+
assert_equal 'snowball', DummyRepository.mappings.to_hash[:my_dummy_model][:properties][:title][:analyzer]
55+
56+
assert_equal :foobar, repository.index
57+
assert_equal DummyModel, repository.klass
58+
assert_equal :my_dummy_model, repository.type
59+
assert_equal 'snowball', repository.mappings.to_hash[:my_dummy_model][:properties][:title][:analyzer]
60+
end
61+
62+
should "proxy repository methods from the instance to the gateway" do
63+
class DummyRepository
64+
include Elasticsearch::Persistence::Repository
65+
end
66+
67+
repository = DummyRepository.new
68+
repository.index :foobar
69+
repository.klass DummyModel
70+
repository.type :my_dummy_model
71+
repository.mapping { indexes :title, analyzer: 'snowball' }
72+
73+
assert_equal :foobar, DummyRepository.index
74+
assert_equal DummyModel, DummyRepository.klass
75+
assert_equal :my_dummy_model, DummyRepository.type
76+
assert_equal 'snowball', DummyRepository.mappings.to_hash[:my_dummy_model][:properties][:title][:analyzer]
77+
78+
assert_equal :foobar, repository.index
79+
assert_equal DummyModel, repository.klass
80+
assert_equal :my_dummy_model, repository.type
81+
assert_equal 'snowball', repository.mappings.to_hash[:my_dummy_model][:properties][:title][:analyzer]
82+
end
83+
84+
should "allow to define gateway methods in the class definition" do
85+
class DummyRepository
86+
include Elasticsearch::Persistence::Repository
87+
88+
gateway do
89+
def serialize(document)
90+
'FAKE!'
91+
end
92+
end
93+
end
94+
95+
repository = DummyRepository.new
96+
repository.client.transport.logger = Logger.new(STDERR)
97+
98+
client = mock
99+
client.expects(:index).with do |arguments|
100+
assert_equal('xxx', arguments[:id])
101+
assert_equal('FAKE!', arguments[:body])
102+
end
103+
repository.gateway.expects(:client).returns(client)
104+
105+
repository.gateway.expects(:__get_id_from_document).returns('xxx')
106+
107+
repository.save( id: '123', foo: 'bar' )
108+
end
109+
end
110+
5111
end
6112
end

0 commit comments

Comments
 (0)