gummi 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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