gummi 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +1 -0
  2. data/gummi.gemspec +7 -6
  3. data/lib/gummi.rb +32 -26
  4. data/lib/gummi/api.rb +3 -1
  5. data/lib/gummi/db_layer/default_index.rb +15 -0
  6. data/lib/gummi/db_layer/document.rb +206 -0
  7. data/lib/gummi/db_layer/document/attributes.rb +40 -0
  8. data/lib/gummi/db_layer/document/object.rb +15 -0
  9. data/lib/gummi/db_layer/document/search/filtered.rb +42 -0
  10. data/lib/gummi/db_layer/document/search/raw.rb +12 -0
  11. data/lib/gummi/db_layer/document/search/result.rb +34 -0
  12. data/lib/gummi/db_layer/document/search/searching.rb +51 -0
  13. data/lib/gummi/db_layer/fields/boolean.rb +13 -0
  14. data/lib/gummi/db_layer/fields/integer.rb +16 -0
  15. data/lib/gummi/db_layer/fields/keyword.rb +15 -0
  16. data/lib/gummi/db_layer/fields/ngram_and_plain.rb +20 -0
  17. data/lib/gummi/db_layer/fields/path_hierarchy.rb +15 -0
  18. data/lib/gummi/db_layer/fields/positive_integer.rb +21 -0
  19. data/lib/gummi/db_layer/fields/sanitized_string.rb +30 -0
  20. data/lib/gummi/db_layer/fields/string.rb +17 -0
  21. data/lib/gummi/db_layer/fields/time.rb +17 -0
  22. data/lib/gummi/db_layer/index.rb +150 -0
  23. data/lib/gummi/entity_layer/entity.rb +22 -0
  24. data/lib/gummi/errors.rb +7 -0
  25. data/lib/gummi/repository_layer/repository.rb +39 -0
  26. data/lib/gummi/repository_layer/repository/result.rb +42 -0
  27. data/lib/gummi/version.rb +1 -1
  28. data/lib/repobahn/repository.rb +25 -33
  29. data/lib/repobahn/repository/active_record.rb +17 -0
  30. data/spec/fixtures/admin/auto.rb +6 -0
  31. data/spec/fixtures/admin/cars.rb +12 -0
  32. data/spec/fixtures/admin/countries.rb +9 -0
  33. data/spec/fixtures/admin/country.rb +6 -0
  34. data/spec/fixtures/admin/db/country.rb +7 -0
  35. data/spec/fixtures/admin/db/vehicle.rb +7 -0
  36. data/spec/fixtures/cities.rb +7 -0
  37. data/spec/fixtures/city.rb +6 -0
  38. data/spec/fixtures/db/animal.rb +9 -0
  39. data/spec/fixtures/db/boat.rb +9 -0
  40. data/spec/fixtures/db/car.rb +9 -0
  41. data/spec/fixtures/db/city.rb +8 -0
  42. data/spec/fixtures/db/enemy.rb +10 -0
  43. data/spec/fixtures/db/game.rb +7 -0
  44. data/spec/fixtures/db/person.rb +15 -0
  45. data/spec/fixtures/db/rating.rb +11 -0
  46. data/spec/fixtures/db/ship.rb +18 -0
  47. data/spec/{models → fixtures}/people.rb +6 -2
  48. data/spec/{models → fixtures}/person.rb +3 -2
  49. data/spec/lib/gummi/db_layer/document_spec.rb +124 -0
  50. data/spec/lib/gummi/{entity_spec.rb → entity_layer/entity_spec.rb} +3 -1
  51. data/spec/lib/gummi/repository_layer/repository_spec.rb +63 -0
  52. data/spec/lib/repobahn/repository_spec.rb +72 -0
  53. data/spec/spec_helper.rb +37 -9
  54. metadata +87 -37
  55. data/lib/gummi/default_index.rb +0 -13
  56. data/lib/gummi/document.rb +0 -139
  57. data/lib/gummi/document/attributes.rb +0 -28
  58. data/lib/gummi/document/object.rb +0 -12
  59. data/lib/gummi/document/search/filtered.rb +0 -39
  60. data/lib/gummi/document/search/raw.rb +0 -9
  61. data/lib/gummi/document/search/result.rb +0 -25
  62. data/lib/gummi/document/search/searching.rb +0 -45
  63. data/lib/gummi/entity.rb +0 -20
  64. data/lib/gummi/fields/boolean.rb +0 -10
  65. data/lib/gummi/fields/integer.rb +0 -14
  66. data/lib/gummi/fields/keyword.rb +0 -13
  67. data/lib/gummi/fields/ngram_and_plain.rb +0 -18
  68. data/lib/gummi/fields/path_hierarchy.rb +0 -13
  69. data/lib/gummi/fields/positive_integer.rb +0 -19
  70. data/lib/gummi/fields/sanitized_string.rb +0 -28
  71. data/lib/gummi/fields/string.rb +0 -15
  72. data/lib/gummi/fields/time.rb +0 -15
  73. data/lib/gummi/index.rb +0 -146
  74. data/lib/gummi/repository.rb +0 -38
  75. data/lib/gummi/repository/result.rb +0 -30
  76. data/spec/lib/gummi/document_spec.rb +0 -73
  77. data/spec/lib/gummi/repository/result_spec.rb +0 -18
  78. data/spec/lib/gummi/repository_spec.rb +0 -81
  79. 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
@@ -1,3 +1,3 @@
1
1
  module Gummi
2
- VERSION = "0.1.2"
2
+ VERSION = '0.2.0'
3
3
  end
@@ -2,56 +2,48 @@ module Repobahn
2
2
  module Repository
3
3
  extend ActiveSupport::Concern
4
4
 
5
- included do
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 entity_model
14
- @entity_model || default_entity_model
15
- end
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 db_model=(klass)
26
- @db_model = klass
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 find(id)
30
- record = db_model.find id
31
- to_entity_from_db record if record
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 to_entity_from_db(records)
35
- entities = Array(records).map do |record|
36
- entity = entity_model.new(record.attributes)
37
- run_hook :after_conversion, entity, record
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 default_entity_model
46
- full_name = name.split('::')
47
- model_name = full_name.pop.singularize
48
- full_name << model_name
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 default_db_model
53
- "DB::#{name.split('::').last.singularize}".constantize
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,6 @@
1
+ module Admin
2
+ class Auto
3
+ include Repobahn::Entity
4
+
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ require 'admin/db/vehicle'
2
+ require 'admin/auto'
3
+
4
+ module Admin
5
+ class Cars
6
+ include Repobahn::Repository
7
+
8
+ db_class DB::Vehicle
9
+ entity_class Auto
10
+
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require 'admin/db/country'
2
+ require 'admin/country'
3
+
4
+ module Admin
5
+ class Countries
6
+ include Repobahn::Repository
7
+
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Admin
2
+ class Country
3
+ include Repobahn::Entity
4
+
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Admin
2
+ module DB
3
+ class Country
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Admin
2
+ module DB
3
+ class Vehicle
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'db/city'
2
+ require 'city'
3
+
4
+ class Cities
5
+ include Repobahn::Repository
6
+
7
+ end
@@ -0,0 +1,6 @@
1
+ class City
2
+ include Repobahn::Entity
3
+
4
+ attribute :name, String
5
+
6
+ end
@@ -0,0 +1,9 @@
1
+ # Only defining a custom document_type
2
+
3
+ module DB
4
+ class Animal
5
+ include Gummi::DbLayer::Document
6
+
7
+ document_type :wild_animal
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # Sub-Object Type
2
+
3
+ module DB
4
+ class Boat
5
+ include Gummi::DbLayer::Document::Object
6
+
7
+ attribute :length, Gummi::DbLayer::Fields::PositiveInteger
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # Has a custom Index
2
+
3
+ module DB
4
+ class Car
5
+ include Gummi::DbLayer::Document
6
+
7
+ index Struct
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module DB
2
+ class City
3
+ include Virtus.model
4
+
5
+ attribute :name, String
6
+
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ # Not defining the attribute type
2
+
3
+ module DB
4
+ class Enemy
5
+ include Gummi::DbLayer::Document
6
+
7
+ attribute :rebel
8
+
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # Blank as can be, just the inclusion.
2
+
3
+ module DB
4
+ class Game
5
+ include Gummi::DbLayer::Document
6
+ end
7
+ 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,11 @@
1
+ # Is a child document
2
+
3
+ module DB
4
+ class Rating
5
+ include Gummi::DbLayer::Document
6
+
7
+ parent Person
8
+ attribute :stars, Gummi::DbLayer::Fields::Integer
9
+
10
+ end
11
+ 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
- end
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