gummi 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/gummi.gemspec +7 -6
- data/lib/gummi.rb +32 -26
- data/lib/gummi/api.rb +3 -1
- data/lib/gummi/db_layer/default_index.rb +15 -0
- data/lib/gummi/db_layer/document.rb +206 -0
- data/lib/gummi/db_layer/document/attributes.rb +40 -0
- data/lib/gummi/db_layer/document/object.rb +15 -0
- data/lib/gummi/db_layer/document/search/filtered.rb +42 -0
- data/lib/gummi/db_layer/document/search/raw.rb +12 -0
- data/lib/gummi/db_layer/document/search/result.rb +34 -0
- data/lib/gummi/db_layer/document/search/searching.rb +51 -0
- data/lib/gummi/db_layer/fields/boolean.rb +13 -0
- data/lib/gummi/db_layer/fields/integer.rb +16 -0
- data/lib/gummi/db_layer/fields/keyword.rb +15 -0
- data/lib/gummi/db_layer/fields/ngram_and_plain.rb +20 -0
- data/lib/gummi/db_layer/fields/path_hierarchy.rb +15 -0
- data/lib/gummi/db_layer/fields/positive_integer.rb +21 -0
- data/lib/gummi/db_layer/fields/sanitized_string.rb +30 -0
- data/lib/gummi/db_layer/fields/string.rb +17 -0
- data/lib/gummi/db_layer/fields/time.rb +17 -0
- data/lib/gummi/db_layer/index.rb +150 -0
- data/lib/gummi/entity_layer/entity.rb +22 -0
- data/lib/gummi/errors.rb +7 -0
- data/lib/gummi/repository_layer/repository.rb +39 -0
- data/lib/gummi/repository_layer/repository/result.rb +42 -0
- data/lib/gummi/version.rb +1 -1
- data/lib/repobahn/repository.rb +25 -33
- data/lib/repobahn/repository/active_record.rb +17 -0
- data/spec/fixtures/admin/auto.rb +6 -0
- data/spec/fixtures/admin/cars.rb +12 -0
- data/spec/fixtures/admin/countries.rb +9 -0
- data/spec/fixtures/admin/country.rb +6 -0
- data/spec/fixtures/admin/db/country.rb +7 -0
- data/spec/fixtures/admin/db/vehicle.rb +7 -0
- data/spec/fixtures/cities.rb +7 -0
- data/spec/fixtures/city.rb +6 -0
- data/spec/fixtures/db/animal.rb +9 -0
- data/spec/fixtures/db/boat.rb +9 -0
- data/spec/fixtures/db/car.rb +9 -0
- data/spec/fixtures/db/city.rb +8 -0
- data/spec/fixtures/db/enemy.rb +10 -0
- data/spec/fixtures/db/game.rb +7 -0
- data/spec/fixtures/db/person.rb +15 -0
- data/spec/fixtures/db/rating.rb +11 -0
- data/spec/fixtures/db/ship.rb +18 -0
- data/spec/{models → fixtures}/people.rb +6 -2
- data/spec/{models → fixtures}/person.rb +3 -2
- data/spec/lib/gummi/db_layer/document_spec.rb +124 -0
- data/spec/lib/gummi/{entity_spec.rb → entity_layer/entity_spec.rb} +3 -1
- data/spec/lib/gummi/repository_layer/repository_spec.rb +63 -0
- data/spec/lib/repobahn/repository_spec.rb +72 -0
- data/spec/spec_helper.rb +37 -9
- metadata +87 -37
- data/lib/gummi/default_index.rb +0 -13
- data/lib/gummi/document.rb +0 -139
- data/lib/gummi/document/attributes.rb +0 -28
- data/lib/gummi/document/object.rb +0 -12
- data/lib/gummi/document/search/filtered.rb +0 -39
- data/lib/gummi/document/search/raw.rb +0 -9
- data/lib/gummi/document/search/result.rb +0 -25
- data/lib/gummi/document/search/searching.rb +0 -45
- data/lib/gummi/entity.rb +0 -20
- data/lib/gummi/fields/boolean.rb +0 -10
- data/lib/gummi/fields/integer.rb +0 -14
- data/lib/gummi/fields/keyword.rb +0 -13
- data/lib/gummi/fields/ngram_and_plain.rb +0 -18
- data/lib/gummi/fields/path_hierarchy.rb +0 -13
- data/lib/gummi/fields/positive_integer.rb +0 -19
- data/lib/gummi/fields/sanitized_string.rb +0 -28
- data/lib/gummi/fields/string.rb +0 -15
- data/lib/gummi/fields/time.rb +0 -15
- data/lib/gummi/index.rb +0 -146
- data/lib/gummi/repository.rb +0 -38
- data/lib/gummi/repository/result.rb +0 -30
- data/spec/lib/gummi/document_spec.rb +0 -73
- data/spec/lib/gummi/repository/result_spec.rb +0 -18
- data/spec/lib/gummi/repository_spec.rb +0 -81
- data/spec/models/db/person.rb +0 -15
@@ -0,0 +1,39 @@
|
|
1
|
+
module Gummi
|
2
|
+
module RepositoryLayer
|
3
|
+
module Repository
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include Repobahn::Repository
|
8
|
+
after_conversion :set_id_and_version
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def get(id)
|
14
|
+
document = db_class.get id
|
15
|
+
db_instance_to_entity document if document
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(&block)
|
19
|
+
search = db_class.new_filtered_search
|
20
|
+
yield search
|
21
|
+
Repository::Result.new search.execute, self
|
22
|
+
end
|
23
|
+
|
24
|
+
def overwrite(entity)
|
25
|
+
return false unless entity.valid?
|
26
|
+
document = db_class.new(entity.attributes)
|
27
|
+
document.overwrite
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_id_and_version(entity, db_instance)
|
31
|
+
entity.id = db_instance.id
|
32
|
+
entity.version = db_instance.version
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Gummi
|
2
|
+
module RepositoryLayer
|
3
|
+
module Repository
|
4
|
+
class Result
|
5
|
+
|
6
|
+
attr_reader :took, :total, :hits
|
7
|
+
|
8
|
+
def initialize(search_result, converter)
|
9
|
+
@search_result = search_result
|
10
|
+
@took = @search_result.took
|
11
|
+
@documents = @search_result.documents
|
12
|
+
@converter = converter
|
13
|
+
end
|
14
|
+
|
15
|
+
def facets
|
16
|
+
@facets ||= begin
|
17
|
+
response.facets.each_with_object({}) do |(name, content), facets|
|
18
|
+
facets[name] = Hash[content.terms.map(&:values)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def total
|
24
|
+
documents.total_entries
|
25
|
+
end
|
26
|
+
|
27
|
+
def records
|
28
|
+
@records ||= begin
|
29
|
+
# Passing through the Leaflet Collection, but converting the records.
|
30
|
+
documents.map! { |document| converter.db_instance_to_entity(document) }
|
31
|
+
documents
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :search_result, :converter, :documents
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/gummi/version.rb
CHANGED
data/lib/repobahn/repository.rb
CHANGED
@@ -2,56 +2,48 @@ module Repobahn
|
|
2
2
|
module Repository
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
6
|
-
include Hooks
|
7
|
-
define_hook :after_conversion
|
8
|
-
end
|
5
|
+
include ::Repobahn::Repository::ActiveRecord
|
9
6
|
|
7
|
+
included do
|
8
|
+
include Hooks
|
9
|
+
define_hook :after_conversion
|
10
|
+
end
|
10
11
|
|
11
12
|
module ClassMethods
|
12
13
|
|
13
|
-
def
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
def entity_model=(klass)
|
18
|
-
@entity_model = klass
|
19
|
-
end
|
20
|
-
|
21
|
-
def db_model
|
22
|
-
@db_model || default_db_model
|
14
|
+
def db_class(*args)
|
15
|
+
@db_class = args.first unless args.empty?
|
16
|
+
@db_class || default_db_class
|
23
17
|
end
|
24
18
|
|
25
|
-
def
|
26
|
-
@
|
19
|
+
def entity_class(*args)
|
20
|
+
@entity_class = args.first unless args.empty?
|
21
|
+
@entity_class || default_entity_class
|
27
22
|
end
|
28
23
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
24
|
+
def db_instances_to_entities(db_instances)
|
25
|
+
entities = Array(db_instances).map { |db_instance| db_instance_to_entity(db_instance) }
|
26
|
+
entities.length > 1 ? entities : entities.first
|
32
27
|
end
|
33
28
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
entity
|
39
|
-
end
|
40
|
-
entities.length > 1 ? entities : entities.first
|
29
|
+
def db_instance_to_entity(db_instance)
|
30
|
+
entity = entity_class.new(db_instance.attributes)
|
31
|
+
run_hook :after_conversion, entity, db_instance
|
32
|
+
entity
|
41
33
|
end
|
42
34
|
|
43
35
|
private
|
44
36
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
full_name.join('::').constantize
|
37
|
+
def default_db_class
|
38
|
+
parts = default_entity_class.to_s.split('::')
|
39
|
+
parts.insert -2, 'DB'
|
40
|
+
parts.join('::').constantize
|
50
41
|
end
|
51
42
|
|
52
|
-
def
|
53
|
-
|
43
|
+
def default_entity_class
|
44
|
+
name.singularize.constantize
|
54
45
|
end
|
46
|
+
|
55
47
|
end
|
56
48
|
end
|
57
49
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Repobahn
|
2
|
+
module Repository
|
3
|
+
module ActiveRecord
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def find(id)
|
9
|
+
record = document_class.find id.to_i
|
10
|
+
document_to_entity record if record
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# example db model
|
2
|
+
|
3
|
+
module DB
|
4
|
+
class Person
|
5
|
+
include Gummi::DbLayer::Document
|
6
|
+
|
7
|
+
attribute :name, Gummi::DbLayer::Fields::String
|
8
|
+
attribute :computed_name, Gummi::DbLayer::Fields::String
|
9
|
+
attribute :born_at, Gummi::DbLayer::Fields::Time
|
10
|
+
|
11
|
+
def computed_name
|
12
|
+
name.upcase if name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Various attribute field types
|
2
|
+
|
3
|
+
require 'db/boat'
|
4
|
+
|
5
|
+
module DB
|
6
|
+
class Ship
|
7
|
+
include Gummi::DbLayer::Document
|
8
|
+
|
9
|
+
attribute :name, Gummi::DbLayer::Fields::String
|
10
|
+
attribute :tags, Gummi::DbLayer::Fields::Keyword
|
11
|
+
attribute :category, Gummi::DbLayer::Fields::PathHierarchy
|
12
|
+
attribute :height, Gummi::DbLayer::Fields::PositiveInteger
|
13
|
+
attribute :unsinkable, Gummi::DbLayer::Fields::Boolean
|
14
|
+
attribute :freetext, Gummi::DbLayer::Fields::NgramAndPlain
|
15
|
+
attribute :boat, Boat
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'db/person'
|
2
|
+
require 'person'
|
3
|
+
|
1
4
|
class People
|
2
|
-
include Gummi::Repository
|
5
|
+
include Gummi::RepositoryLayer::Repository
|
3
6
|
|
4
7
|
after_conversion :convert_name
|
5
8
|
|
6
9
|
def self.convert_name(entity, db)
|
7
10
|
entity.converted_name = db.name.reverse
|
8
11
|
end
|
9
|
-
|
12
|
+
|
13
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# example entity model
|
2
|
+
|
2
3
|
class Person
|
3
4
|
|
4
5
|
class Car
|
5
|
-
include Gummi::Entity
|
6
|
+
include Gummi::EntityLayer::Entity
|
6
7
|
|
7
8
|
attribute :model, String
|
8
9
|
end
|
9
10
|
|
10
|
-
include Gummi::Entity
|
11
|
+
include Gummi::EntityLayer::Entity
|
11
12
|
|
12
13
|
attribute :name, String
|
13
14
|
attribute :converted_name, String
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'db/animal'
|
4
|
+
require 'db/car'
|
5
|
+
require 'db/enemy'
|
6
|
+
require 'db/game'
|
7
|
+
require 'db/person'
|
8
|
+
require 'db/rating'
|
9
|
+
require 'db/ship'
|
10
|
+
|
11
|
+
describe Gummi::DbLayer::Document do
|
12
|
+
|
13
|
+
context 'included' do
|
14
|
+
it 'adds accessors for id and version' do
|
15
|
+
model = DB::Ship.new
|
16
|
+
model.should respond_to :id
|
17
|
+
model.should respond_to :version
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'adds default attributes methods' do
|
21
|
+
model = DB::Ship.new name: 'Joe'
|
22
|
+
model.name.should == 'Joe'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'does not accept attributes without explicit type definition' do
|
26
|
+
expect { DB::Enemy.sync_mapping! }.to raise_error(Gummi::Errors::ImplicitMappingForbidden)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.index' do
|
31
|
+
context 'without being specified' do
|
32
|
+
it 'defaults to the default index' do
|
33
|
+
DB::Game.index.should == Gummi::DbLayer::DefaultIndex
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'explicitly specified' do
|
38
|
+
it 'accepts the specification' do
|
39
|
+
DB::Car.index.should == Struct
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.document_type' do
|
45
|
+
context 'without being specified' do
|
46
|
+
it 'defaults to the demodulized name' do
|
47
|
+
DB::Game.document_type.should == :game
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'explicitly specified' do
|
52
|
+
it 'accepts the specification' do
|
53
|
+
DB::Animal.document_type.should == :wild_animal
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'attributes' do
|
59
|
+
|
60
|
+
context 'date times' do
|
61
|
+
it 'coerces from elastics strings to real Time' do
|
62
|
+
time = Time.now
|
63
|
+
person = DB::Person.new(born_at: time)
|
64
|
+
person.overwrite
|
65
|
+
person_from_es = DB::Person.get person.id
|
66
|
+
person_from_es.born_at.should be_a Time
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'always stores time in UTC' do
|
70
|
+
time = Time.now.in_time_zone 'CET'
|
71
|
+
|
72
|
+
person = DB::Person.new(born_at: time)
|
73
|
+
person.born_at.zone.should == 'UTC'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'computed_attributes' do
|
78
|
+
|
79
|
+
let(:person) { DB::Person.new }
|
80
|
+
|
81
|
+
it 'adds them to the attributes hash' do
|
82
|
+
person.name = 'olof palme'
|
83
|
+
person.attributes[:computed_name].should == 'OLOF PALME'
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'computes every time' do
|
87
|
+
person.name = 'olof palme'
|
88
|
+
person.computed_name.should == person.name.upcase
|
89
|
+
person.name = 'carl bildt'
|
90
|
+
person.computed_name.should == person.name.upcase
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'provides a mapping' do
|
94
|
+
DB::Person.mapping.should include(computed_name: { type: 'string' })
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'getting from elastic' do
|
100
|
+
let(:person) { DB::Person.create(name: 'Buzz Lightyear') }
|
101
|
+
|
102
|
+
it 'returns an instance of the document_class' do
|
103
|
+
person_from_es = DB::Person.get(person.id)
|
104
|
+
person_from_es.should be_a DB::Person
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'child documents' do
|
109
|
+
let(:person) { DB::Person.create name: 'Superman' }
|
110
|
+
let(:rating) { DB::Rating.create person_id: person.id, stars: 5 }
|
111
|
+
|
112
|
+
it 'creates and retrieves child documents' do
|
113
|
+
persisted_rating = DB::Rating.get(rating.id, person.id)
|
114
|
+
persisted_rating.person_id.should == person.id
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#get' do
|
118
|
+
it 'demands the parent_id' do
|
119
|
+
expect { DB::Rating.get(rating.id) }.to raise_error(ArgumentError)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|