elos 0.1.0 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/bin/push +40 -0
  4. data/bin/rspec +16 -0
  5. data/db/migrate/001_create_a_entry_repos.rb +7 -0
  6. data/elos-0.1.0.gem +0 -0
  7. data/elos-1.0.3.gem +0 -0
  8. data/elos.gemspec +20 -9
  9. data/lib/elos/configuration.rb +16 -0
  10. data/lib/elos/criteria.rb +78 -0
  11. data/lib/elos/data_object.rb +30 -0
  12. data/lib/elos/errors/not_found.rb +5 -0
  13. data/lib/elos/errors/validation_failed.rb +5 -0
  14. data/lib/elos/index/core.rb +35 -0
  15. data/lib/elos/index/indexable.rb +32 -0
  16. data/lib/elos/index/locatable.rb +17 -0
  17. data/lib/elos/index/mappings.rb +25 -0
  18. data/lib/elos/index/model/assignable.rb +19 -0
  19. data/lib/elos/index/model/identifiable.rb +9 -0
  20. data/lib/elos/index/model/initializable.rb +13 -0
  21. data/lib/elos/index/model.rb +12 -0
  22. data/lib/elos/index/raw_helpers.rb +9 -0
  23. data/lib/elos/index/refreshable.rb +9 -0
  24. data/lib/elos/index/reindexable.rb +132 -0
  25. data/lib/elos/index/searchable.rb +17 -0
  26. data/lib/elos/index/subscribable.rb +14 -0
  27. data/lib/elos/index/unindexable.rb +9 -0
  28. data/lib/elos/index.rb +15 -0
  29. data/lib/elos/lock.rb +48 -0
  30. data/lib/elos/query_builder/base.rb +23 -0
  31. data/lib/elos/query_builder/builtin/match_all_query_builder.rb +5 -0
  32. data/lib/elos/query_builder/builtin/where_query_builder.rb +5 -0
  33. data/lib/elos/query_builder/filters.rb +41 -0
  34. data/lib/elos/query_builder/queries.rb +15 -0
  35. data/lib/elos/repository/adapter/active_record/scanable.rb +11 -0
  36. data/lib/elos/repository/adapter/active_record.rb +6 -0
  37. data/lib/elos/repository/adapter/elos/migratable.rb +13 -0
  38. data/lib/elos/repository/adapter/elos/model/destroyable.rb +44 -0
  39. data/lib/elos/repository/adapter/elos/model/persistable.rb +79 -0
  40. data/lib/elos/repository/adapter/elos/model/reindexable.rb +15 -0
  41. data/lib/elos/repository/adapter/elos/model/unindexable.rb +7 -0
  42. data/lib/elos/repository/adapter/elos/model/validatable.rb +8 -0
  43. data/lib/elos/repository/adapter/elos/scanable.rb +24 -0
  44. data/lib/elos/repository/adapter/elos.rb +31 -0
  45. data/lib/elos/repository/publishable.rb +26 -0
  46. data/lib/elos/result.rb +23 -0
  47. data/lib/elos/version.rb +2 -2
  48. data/lib/elos.rb +3 -3
  49. data/lib/initialize.rb +6 -0
  50. metadata +129 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbdcf9020cc5fd26078aa94d7baaa1c61f4d5c6a
4
- data.tar.gz: c874a6670ddce992a34e7f1d0788eabf3b5672f4
3
+ metadata.gz: 4c5f6f066aac89f49b12b923b11c03178df3003c
4
+ data.tar.gz: 27028953724446f96f69b59191e6ddf93bb63bd6
5
5
  SHA512:
6
- metadata.gz: e8ab7b916b4aa3c3df901359c80b97a7fd831bb5b60570f62b61850efcce76fcdeab41897dca06ddd2f0d403e6e37610a7a286b1d09ad38891dc21b8aa7fa794
7
- data.tar.gz: 4d7628c6ba01494b0845e394486c62418c6d18f4030e44bcfe72b9d51b23c1772df793101c1324d9abe1fd8c2353b511ae404bbf83e723e793325f5e76cc9ff4
6
+ metadata.gz: 84a7cb6d89f163762c8871a7569c324dcd2d82290fe293fc2102cae665e9e4cef01793cbd1e84d5baae2e8f6fb95270f221f5d12de33e2f2413fff444fd31e8e
7
+ data.tar.gz: ac0abb746a1fc9bb1c56854f6fad3126f030f3e5dc712f503187c172d5c85e87e2ee8f75143b7d020db7d775cce6b9cb3b88b129c6666bbfc809c54261a7f502
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
+ --order rand
4
+ --require spec_helper
data/bin/push ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "elos"
5
+
6
+ def versions(version_string)
7
+ vers = version_string.split('.').map(&:to_i)
8
+ fail 'Invalid version format!' unless vers.length == 3
9
+ vers
10
+ end
11
+
12
+ def later_version!(vers1, vers2)
13
+ [vers1, vers2].transpose.each do |ver1, ver2|
14
+ return if ver1 > ver2
15
+ fail 'Not later version!' if ver1 < ver2
16
+ end
17
+ fail 'Not later version!'
18
+ end
19
+
20
+ specified_vers = versions(ARGV[0]) if ARGV.length > 0
21
+ vers = versions(Elos::VERSION)
22
+ specified_vers ||=
23
+ begin
24
+ r = vers.dup
25
+ r[2] = r[2] + 1
26
+ r
27
+ end
28
+
29
+ later_version!(specified_vers, vers)
30
+
31
+ path = File.expand_path('../..', __FILE__)
32
+ new_ver = specified_vers.join('.')
33
+ `sed -i -e 's/#{Elos::VERSION}/#{new_ver}/g' #{path}/lib/elos/version.rb`
34
+ `git add --all`
35
+ `git commit -m 'version #{new_ver}'`
36
+ `git push origin master`
37
+ `git tag #{new_ver}`
38
+ `git push origin #{new_ver}`
39
+ `gem build elos.gemspec`
40
+ `gem push elos-#{new_ver}.gem`
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,7 @@
1
+ class CreateAEntryRepos < ActiveRecord::Migration
2
+ def change
3
+ create_table :a_entry_repos do |t|
4
+ t.string :title
5
+ end
6
+ end
7
+ end
data/elos-0.1.0.gem ADDED
Binary file
data/elos-1.0.3.gem ADDED
Binary file
data/elos.gemspec CHANGED
@@ -4,21 +4,32 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'elos/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "elos"
7
+ spec.name = 'elos'
8
8
  spec.version = Elos::VERSION
9
- spec.authors = ["Tetsuri Moriya"]
10
- spec.email = ["tetsuri.moriya@gmail.com"]
9
+ spec.authors = ['Tetsuri Moriya']
10
+ spec.email = ['tetsuri.moriya@gmail.com']
11
11
 
12
12
  spec.summary = %q{Elasticsearch integration for Databases}
13
13
  spec.description = %q{Elasticsearch integration for Databases, such as ActiveRecord}
14
- spec.homepage = "https://github.com/pandora2000/elos"
15
- spec.license = "MIT"
14
+ spec.homepage = 'https://github.com/pandora2000/elos'
15
+ spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
18
+ spec.bindir = 'exe'
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
21
 
22
- spec.add_development_dependency "bundler", "~> 1.9"
23
- spec.add_development_dependency "rake", "~> 10.0"
22
+ [['activesupport', '>= 3.0.0'],
23
+ ['elasticsearch']].each do |args|
24
+ spec.add_dependency *args
25
+ end
26
+
27
+ [['bundler', '~> 1.9'],
28
+ ['rake', '~> 10.0'],
29
+ ['rspec', '>= 0'],
30
+ ['database_cleaner', '~> 1.2.0'],
31
+ ['activerecord'],
32
+ ['mysql2']].each do |args|
33
+ spec.add_development_dependency *args
34
+ end
24
35
  end
@@ -0,0 +1,16 @@
1
+ module Elos::Configuration
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_accessor :client, :env, :settings
6
+ end
7
+
8
+ class_methods do
9
+ def config(host: nil, env: nil, settings: {})
10
+ host ||= 'localhost:9200'
11
+ self.client = ::Elasticsearch::Client.new(host: host)
12
+ self.env = env
13
+ self.settings = settings
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,78 @@
1
+ class Elos::Criteria < Elos::Result
2
+ def initialize(params:, query_builder_class:, klass:)
3
+ @client = Elos.client
4
+ @params = params
5
+ @query_builder_class = query_builder_class
6
+ @klass = klass
7
+ end
8
+
9
+ def page(page)
10
+ @params[:page] = page
11
+ self
12
+ end
13
+
14
+ def per(per = nil)
15
+ if per
16
+ @params[:per] = per
17
+ self
18
+ else
19
+ @params[:per]
20
+ end
21
+ end
22
+
23
+ alias_method :limit, :per
24
+
25
+ def includes(*names)
26
+ @includes = names
27
+ self
28
+ end
29
+
30
+ def total_count
31
+ result
32
+ @total_count
33
+ end
34
+
35
+ def current_page
36
+ @params[:page]
37
+ end
38
+
39
+ def total_pages
40
+ [0, total_count - 1].max / @params[:per] + 1
41
+ end
42
+
43
+ def first_page?
44
+ current_page == 1
45
+ end
46
+
47
+ def last_page?
48
+ current_page == total_pages
49
+ end
50
+
51
+ def to_a!
52
+ r = to_a
53
+ raise Elos::Errors::NotFound if r.empty?
54
+ r
55
+ end
56
+
57
+ def first!
58
+ r = first
59
+ raise Elos::Errors::NotFound unless r
60
+ r
61
+ end
62
+
63
+ def query
64
+ @query_builder_class.new(@params)._build
65
+ end
66
+
67
+ protected
68
+
69
+ def result
70
+ return @result if @result
71
+ search_result = @client.search(index: @klass.read_alias_name, type: @klass.type_name, body: query)
72
+ # puts JSON.pretty_generate(results)
73
+ @total_count = search_result['hits']['total']
74
+ @results = search_result['hits']['hits'].map do |h|
75
+ @klass.new(h['_source'].reverse_merge(score: h['_score'], highlight: h['highlight']).merge(id: h['_id'], _persisted: true))
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,30 @@
1
+ class Elos::DataObject
2
+ attr_reader :object
3
+
4
+ def initialize(params = {})
5
+ @params = params
6
+ @object = objectify(params)
7
+ end
8
+
9
+ def method_missing(method, *args, **hargs, &block)
10
+ if object.respond_to?(method)
11
+ object.send(method, *args)
12
+ elsif method.to_s.end_with?('=')
13
+ object.send(method, *args)
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def objectify(value)
22
+ if value.is_a?(Hash)
23
+ OpenStruct.new(Hash[value.map { |k, v| [k, objectify(v)] }])
24
+ elsif value.is_a?(Array)
25
+ value.map { |v| objectify(v) }
26
+ else
27
+ value
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ class Elos::Errors::NotFound < StandardError
2
+ def initialize
3
+ super('Not found.')
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Elos::Errors::ValidationFailed < StandardError
2
+ def initialize
3
+ super('Validation failed.')
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ module Elos::Index::Core
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_accessor :client, :type_name, :index_name
6
+
7
+ self.client = Elos.client
8
+ self.type_name = name.underscore.gsub('/', '-')
9
+ self.index_name = "#{name.underscore.pluralize.gsub('/', '-')}=#{Elos.env}="
10
+ end
11
+
12
+ class_methods do
13
+ protected
14
+
15
+ def create_index(name)
16
+ unless client.indices.exists(index: name)
17
+ client.indices.create(index: name, body: index_parameters)
18
+ end
19
+ end
20
+
21
+ def delete_index(name)
22
+ if client.indices.exists(index: name)
23
+ client.indices.delete(index: name)
24
+ end
25
+ end
26
+
27
+ def refresh_index(name)
28
+ client.indices.refresh(index: name)
29
+ end
30
+
31
+ def index_parameters
32
+ { settings: Elos.settings, mappings: mappings }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Elos::Index::Indexable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_writer :index_data
6
+ end
7
+
8
+ class_methods do
9
+ def index_data(index_data = nil)
10
+ if index_data
11
+ self.index_data = index_data
12
+ else
13
+ self.class_variable_get(:@@index_data)
14
+ end
15
+ end
16
+
17
+ def index(obj, destroy: false, unindex: false)
18
+ if !unindex && data = index_data.(obj)
19
+ data.merge!(_destroyed: destroy)
20
+ params = { index: write_alias_name, type: type_name, body: data }
21
+ params.merge!(id: obj.id) if obj.id
22
+ client.index(params)['_id']
23
+ else
24
+ begin
25
+ client.delete(index: write_alias_name, type: type_name, id: obj.id)
26
+ nil
27
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ module Elos::Index::Locatable
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def where(params = {})
6
+ search(query_builder_class: Elos::QueryBuilder::Builtin::WhereQueryBuilder, **params)
7
+ end
8
+
9
+ def find(arg)
10
+ if arg.is_a?(Array)
11
+ where(id: arg).to_a!
12
+ else
13
+ where(id: [arg]).first!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Elos::Index::Mappings
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_writer :mappings
6
+ end
7
+
8
+ class_methods do
9
+ def mappings(mappings = nil)
10
+ if mappings
11
+ set_mappings(mappings)
12
+ else
13
+ self.class_variable_get(:@@mappings)
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def set_mappings(mappings)
20
+ mps = mappings.deep_dup
21
+ mps[type_name][:properties].merge!(_destroyed: { type: 'boolean' })
22
+ self.mappings = mps
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Elos::Index::Model::Assignable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ after_new -> { assign_attributes(@_attrs) }
6
+ end
7
+
8
+ def assign_attributes(attrs)
9
+ attrs.each do |key, value|
10
+ @_data.send("#{key}=", value)
11
+ end
12
+ end
13
+
14
+ def method_missing(method, *args)
15
+ @_data.send(method, *args)
16
+ rescue NoMethodError
17
+ super
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Elos::Index::Model::Identifiable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ attr_accessor :id
6
+
7
+ before_new -> { self.id = @_attrs.delete(:id) }
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Elos::Index::Model::Initializable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ define_model_callbacks :new
6
+ end
7
+
8
+ def initialize(attrs = {})
9
+ @_data = Elos::DataObject.new
10
+ @_attrs = attrs
11
+ run_callbacks :new
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Elos::Index::Model
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include ActiveModel::Model
6
+ include ActiveModel::Callbacks
7
+
8
+ include Elos::Index::Model::Initializable
9
+ include Elos::Index::Model::Assignable
10
+ include Elos::Index::Model::Identifiable
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Elos::Index::RawHelpers
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def raw_get(id)
6
+ client.get(index: read_alias_name, type: type_name, id: id)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Elos::Index::Refreshable
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def refresh
6
+ refresh_index(read_alias_name)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,132 @@
1
+ module Elos::Index::Reindexable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_accessor :read_alias_name, :write_alias_name, :reindex_block
6
+
7
+ self.read_alias_name = "#{index_name}read"
8
+ self.write_alias_name = "#{index_name}write"
9
+ end
10
+
11
+ class_methods do
12
+ def initialize_for_reindex(force = false)
13
+ return false if !force && client.indices.exists_alias(name: read_alias_name) && client.indices.exists_alias(name: write_alias_name)
14
+ Elos::Lock.unlock(lock_key) if force
15
+ delete_index("#{index_name}*")
16
+ index_name = random_index_name
17
+ unless client.indices.exists(index: index_name)
18
+ client.indices.create(index: index_name, body: index_parameters)
19
+ alias_index(read_alias_name, index_name)
20
+ alias_index(write_alias_name, index_name)
21
+ return true
22
+ end
23
+ false
24
+ end
25
+
26
+ def reindex(set_block = nil, &block)
27
+ return set_reindex(set_block) if set_block
28
+ proc = block || self.reindex_block
29
+ long_args = proc.parameters.length == 2
30
+ Elos::Lock.lock(lock_key) do
31
+ initialize_for_reindex
32
+ old_index_name = read_index_name
33
+ unless write_index_names == [old_index_name]
34
+ repair_for_reindex
35
+ end
36
+ return unless new_index_name = create_index_with_random_name
37
+ begin
38
+ alias_index(write_alias_name, new_index_name)
39
+ if long_args
40
+ proc.(old_index_name, new_index_name)
41
+ else
42
+ proc.()
43
+ end
44
+ switch_pointed_index(read_alias_name, old_index_name, new_index_name)
45
+ unalias_index(write_alias_name, old_index_name)
46
+ rescue Exception => e
47
+ if read_index_name == old_index_name
48
+ if write_index_names.include?(new_index_name)
49
+ unalias_index(write_alias_name, new_index_name)
50
+ end
51
+ delete_index(new_index_name)
52
+ end
53
+ raise e
54
+ end
55
+ delete_index(old_index_name)
56
+ end
57
+ end
58
+
59
+ protected
60
+
61
+ def read_index_name
62
+ aliased_index_name(read_alias_name)
63
+ end
64
+
65
+ def write_index_name
66
+ write_index_names.first
67
+ end
68
+
69
+ def write_index_names
70
+ aliased_index_names(write_alias_name)
71
+ end
72
+
73
+ def lock_key
74
+ "elos:reindex_or_migrate:#{name.underscore}"
75
+ end
76
+
77
+ def set_reindex(block)
78
+ self.reindex_block = block
79
+ end
80
+
81
+ def repair_for_reindex
82
+ (write_index_names - [read_index_name]).each do |name|
83
+ unalias_index(write_alias_name, name)
84
+ delete_index(name)
85
+ end
86
+ return if write_index_name == read_index_name
87
+ alias_index(write_alias_name, read_index_name)
88
+ end
89
+
90
+ def aliased_index_name(alias_name)
91
+ aliased_index_names(alias_name).first
92
+ end
93
+
94
+ def aliased_index_names(alias_name)
95
+ client.indices.get_alias(index: "#{index_name}*", name: alias_name).keys
96
+ end
97
+
98
+ def alias_index(alias_name, index_name)
99
+ client.indices.update_aliases body: {
100
+ actions: [
101
+ { add: { index: index_name, alias: alias_name } }
102
+ ]
103
+ }
104
+ end
105
+
106
+ def unalias_index(alias_name, index_name)
107
+ client.indices.update_aliases body: {
108
+ actions: [
109
+ { remove: { index: index_name, alias: alias_name } }
110
+ ]
111
+ }
112
+ end
113
+
114
+ def switch_pointed_index(alias_name, from_index_name, to_index_name)
115
+ client.indices.update_aliases body: {
116
+ actions: [
117
+ { remove: { index: from_index_name, alias: alias_name } },
118
+ { add: { index: to_index_name, alias: alias_name } }
119
+ ]
120
+ }
121
+ end
122
+
123
+ def create_index_with_random_name
124
+ create_index(name = random_index_name)
125
+ name
126
+ end
127
+
128
+ def random_index_name
129
+ "#{index_name}#{SecureRandom.uuid}"
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,17 @@
1
+ module Elos::Index::Searchable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_accessor :query_builder_class
6
+ end
7
+
8
+ class_methods do
9
+ def query_builder(klass)
10
+ self.query_builder_class = klass
11
+ end
12
+
13
+ def search(query_builder_class: self.query_builder_class, **params)
14
+ Elos::Criteria.new(params: params, query_builder_class: query_builder_class, klass: self)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module Elos::Index::Subscribable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ cattr_accessor :repository_class
6
+ end
7
+
8
+ class_methods do
9
+ def subscribe(repository_class)
10
+ self.repository_class = repository_class
11
+ repository_class.subscribers << self
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Elos::Index::Unindexable
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def unindex
6
+ initialize_for_reindex(true)
7
+ end
8
+ end
9
+ end
data/lib/elos/index.rb ADDED
@@ -0,0 +1,15 @@
1
+ module Elos::Index
2
+ extend ActiveSupport::Concern
3
+
4
+ include Elos::Index::Core
5
+ include Elos::Index::Mappings
6
+ include Elos::Index::RawHelpers
7
+ include Elos::Index::Refreshable
8
+ include Elos::Index::Indexable
9
+ include Elos::Index::Reindexable
10
+ include Elos::Index::Unindexable
11
+ include Elos::Index::Searchable
12
+ include Elos::Index::Locatable
13
+ include Elos::Index::Subscribable
14
+ include Elos::Index::Model
15
+ end