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
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ tags
data/gummi.gemspec CHANGED
@@ -17,12 +17,13 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency('virtus', '~> 1.0.0')
21
- spec.add_dependency('elasticsearch', '~> 0.4.0')
22
- spec.add_dependency('activesupport', '>= 3.0')
23
- spec.add_dependency('activemodel', '>= 3.0')
24
- spec.add_dependency('hooks', '~>0.3.3')
25
- spec.add_dependency('leaflet')
20
+ spec.add_dependency 'virtus', '~> 1.0.0'
21
+ spec.add_dependency 'elasticsearch', '~> 0.4.0'
22
+ spec.add_dependency 'activesupport', '>= 3.0'
23
+ spec.add_dependency 'activemodel', '>= 3.0'
24
+ spec.add_dependency 'hooks', '~>0.3.3'
25
+ spec.add_dependency 'leaflet'
26
+ spec.add_dependency 'hashie', '>= 2.0.0'
26
27
 
27
28
  spec.add_development_dependency('bundler', '~> 1.3')
28
29
  spec.add_development_dependency('pry')
data/lib/gummi.rb CHANGED
@@ -4,40 +4,46 @@ require 'active_support/core_ext'
4
4
  require 'active_model'
5
5
  require 'hooks'
6
6
  require 'leaflet'
7
+ require 'hashie/mash'
7
8
 
8
- require "repobahn/repository"
9
- require "repobahn/entity"
9
+ require 'repobahn/repository/active_record'
10
+ require 'repobahn/repository'
11
+ require 'repobahn/entity'
10
12
 
11
- require "gummi/version"
12
- require "gummi/api"
13
- require "gummi/document/attributes"
14
- require "gummi/document"
15
- require "gummi/entity"
16
- require "gummi/index"
17
- require "gummi/document/object"
18
- require "gummi/repository"
19
- require "gummi/repository/result"
20
- require "gummi/fields/boolean"
21
- require "gummi/fields/time"
22
- require "gummi/fields/integer"
23
- require "gummi/fields/positive_integer"
24
- require "gummi/fields/keyword"
25
- require "gummi/fields/ngram_and_plain"
26
- require "gummi/fields/path_hierarchy"
27
- require "gummi/fields/string"
28
- require "gummi/fields/sanitized_string"
29
- require "gummi/default_index"
30
- require "gummi/document/search/searching"
31
- require "gummi/document/search/filtered"
32
- require "gummi/document/search/raw"
33
- require "gummi/document/search/result"
13
+ require 'gummi/version'
14
+ require 'gummi/errors'
15
+ require 'gummi/api'
16
+
17
+ require 'gummi/db_layer/index'
18
+ require 'gummi/db_layer/document/attributes'
19
+ require 'gummi/db_layer/document/object'
20
+ require 'gummi/db_layer/document'
21
+ require 'gummi/db_layer/fields/boolean'
22
+ require 'gummi/db_layer/fields/time'
23
+ require 'gummi/db_layer/fields/integer'
24
+ require 'gummi/db_layer/fields/positive_integer'
25
+ require 'gummi/db_layer/fields/keyword'
26
+ require 'gummi/db_layer/fields/ngram_and_plain'
27
+ require 'gummi/db_layer/fields/path_hierarchy'
28
+ require 'gummi/db_layer/fields/string'
29
+ require 'gummi/db_layer/fields/sanitized_string'
30
+ require 'gummi/db_layer/default_index'
31
+ require 'gummi/db_layer/document/search/searching'
32
+ require 'gummi/db_layer/document/search/filtered'
33
+ require 'gummi/db_layer/document/search/raw'
34
+ require 'gummi/db_layer/document/search/result'
35
+
36
+ require 'gummi/repository_layer/repository'
37
+ require 'gummi/repository_layer/repository/result'
38
+
39
+ require 'gummi/entity_layer/entity'
34
40
 
35
41
  module Gummi
36
42
  def self.env
37
43
  if defined? Rails
38
44
  Rails.env
39
45
  else
40
- RAILS_ENV || "development"
46
+ RAILS_ENV || 'development'
41
47
  end
42
48
  end
43
49
  end
data/lib/gummi/api.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  module Gummi
2
2
  module API
3
+
3
4
  def self.client
4
5
  @client ||= ::Elasticsearch::Client.new log: false
5
6
  end
7
+
6
8
  end
7
- end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Gummi
2
+ module DbLayer
3
+ class DefaultIndex
4
+ include Gummi::DbLayer::Index
5
+
6
+ def self.name
7
+ if defined? Rails
8
+ "#{Rails.application.class.name.deconstantize.underscore}_#{Rails.env}"
9
+ else
10
+ "gummi_#{Gummi.env}"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,206 @@
1
+ module Gummi
2
+ module DbLayer
3
+ module Document
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Virtus.model
8
+ include Gummi::DbLayer::Document::Attributes
9
+ end
10
+
11
+ # –––––––––––––
12
+ # Class Methods
13
+ # –––––––––––––
14
+
15
+ module ClassMethods
16
+
17
+ # ––––––––––––––––––––––
18
+ # Public Persistence API
19
+ # ––––––––––––––––––––––
20
+
21
+ def create(*args)
22
+ instance = new(*args)
23
+ instance.create ? instance : nil
24
+ end
25
+
26
+ def get(*args)
27
+ get! *args
28
+ rescue ::Elasticsearch::Transport::Transport::Errors::NotFound
29
+ nil
30
+ end
31
+
32
+ # –––––––––––––––––––––
33
+ # Public Conversion API
34
+ # –––––––––––––––––––––
35
+
36
+ def hits_to_documents(hits)
37
+ documents = [hits].flatten.map { |hit| hit_to_document(hit) }
38
+ documents.length > 1 ? documents : documents.first
39
+ end
40
+
41
+ def hit_to_document(hit)
42
+ attributes = { id: hit._id, version: hit._version }
43
+ attributes.merge!(parent_id_attribute_name => hit.fields._parent) if hit.fields && hit.fields._parent
44
+ attributes.merge! hit._source if hit._source
45
+ self.new attributes
46
+ end
47
+
48
+ # ––––––––––––––––––––––––––––––
49
+ # Index and Document definitions
50
+ # ––––––––––––––––––––––––––––––
51
+
52
+ def index(*args)
53
+ @index = args.first unless args.empty?
54
+ @index || Gummi::DbLayer::DefaultIndex
55
+ end
56
+
57
+ def document_type(*args)
58
+ @document_type = args.first.to_sym unless args.empty?
59
+ @document_type || name.demodulize.underscore.to_sym
60
+ end
61
+
62
+ def parent(model)
63
+ parent_document_type model.document_type
64
+ end
65
+
66
+ def parent_document_type(*args)
67
+ @parent_document_type = args.first unless args.empty?
68
+ parent_id_attribute_name "#{@parent_document_type}_id".to_sym if @parent_document_type && !parent_id_attribute_name
69
+ @parent_document_type
70
+ end
71
+
72
+ def parent_id_attribute_name(*args)
73
+ unless args.empty?
74
+ @parent_id_attribute_name = args.first
75
+ attr_accessor @parent_id_attribute_name
76
+ end
77
+ @parent_id_attribute_name
78
+ end
79
+
80
+ # ––––––––––––––––
81
+ # Public Index API
82
+ # ––––––––––––––––
83
+
84
+ def sync_mapping!
85
+ client.indices.put_mapping creation_options
86
+ end
87
+
88
+ def creation_options
89
+ result = {
90
+ index: index.name,
91
+ type: document_type,
92
+ body: {
93
+ document_type => {
94
+ properties: mapping,
95
+ }
96
+ }
97
+ }
98
+ result[:body][document_type].merge!(_parent: { type: parent_document_type }) if parent_document_type.present?
99
+ result
100
+ end
101
+
102
+ # –––––––––––––––––
103
+ # Public Search API
104
+ # –––––––––––––––––
105
+
106
+ def new_filtered_search(options = {})
107
+ Gummi::DbLayer::Document::Search::Filtered.new default_search_options.merge(options)
108
+ end
109
+
110
+ def new_raw_search(options = {})
111
+ Gummi::DbLayer::Document::Search::Raw.new default_search_options.merge(options)
112
+ end
113
+
114
+ def default_search_options
115
+ {
116
+ document_class: self,
117
+ index: index.name,
118
+ type: document_type,
119
+ }
120
+ end
121
+
122
+ # –––––––––––––––––––––––––––
123
+ # Internal Backend Connection
124
+ # –––––––––––––––––––––––––––
125
+
126
+ def client
127
+ Gummi::API.client
128
+ end
129
+
130
+ private
131
+
132
+ # ––––––––––––––––––––––––
133
+ # Internal Persistence API
134
+ # ––––––––––––––––––––––––
135
+
136
+ def get!(id, parent_id = nil)
137
+ options = { index: index.name, type: document_type, id: id, fields: %w{ _source _parent } }
138
+ if parent_id
139
+ options.merge! parent: parent_id
140
+ elsif parent_document_type
141
+ raise ArgumentError, "The parent_id attribute is required for getting #{name} from Elastic Search"
142
+ end
143
+ response = Hashie::Mash.new client.get options
144
+ hit_to_document response
145
+ end
146
+
147
+ end
148
+
149
+ # ––––––––––––––––
150
+ # Instance Methods
151
+ # ––––––––––––––––
152
+
153
+ attr_accessor :id
154
+ attr_accessor :version
155
+
156
+ # ––––––––––––––––––––––
157
+ # Public Persistence API
158
+ # ––––––––––––––––––––––
159
+
160
+ def create(method = :create)
161
+ attributes = self.attributes
162
+ if parent_id_attribute_name && parent_id = self.send(parent_id_attribute_name)
163
+ options = { parent: parent_id }
164
+ else
165
+ options = {}
166
+ end
167
+ raise ArgumentError unless [:create, :index].include?(method)
168
+ response = Hashie::Mash.new client.send(method, options.merge(index: index.name, type: document_type, id: self.id, body: attributes))
169
+ if response.ok
170
+ self.id = response._id
171
+ self.version = response._version
172
+ true
173
+ else
174
+ false
175
+ end
176
+ end
177
+
178
+ def overwrite
179
+ create :index
180
+ end
181
+
182
+ private
183
+
184
+ # ––––––––––––––––––––
185
+ # Class method proxies
186
+ # ––––––––––––––––––––
187
+
188
+ def index
189
+ self.class.index
190
+ end
191
+
192
+ def document_type
193
+ self.class.document_type
194
+ end
195
+
196
+ def parent_id_attribute_name
197
+ self.class.parent_id_attribute_name
198
+ end
199
+
200
+ def client
201
+ self.class.client
202
+ end
203
+
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,40 @@
1
+ module Gummi
2
+ module DbLayer
3
+ module Document
4
+ module Attributes
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def mapping_for_attribute(attribute)
10
+ if attribute.is_a? Virtus::Attribute::EmbeddedValue
11
+ { properties: attribute.primitive.mapping }
12
+ elsif attribute.is_a? Virtus::Attribute::Collection
13
+ mapping_for_attribute(attribute.member_type)
14
+ else
15
+ begin
16
+ attribute.mapping
17
+ rescue NoMethodError => exception
18
+ if exception.message.include?("`mapping'")
19
+ raise Errors::ImplicitMappingForbidden, "Sorry, you have to explicitly define the attribute type for #{attribute.instance_variable_name rescue nil} in #{self} or maybe you tried to use Array[] where it is not allowed. Original Exception: #{exception.message}"
20
+ else
21
+ raise exception
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def mapping
28
+ result = {}
29
+ attribute_set.each do |attribute|
30
+ result.merge! attribute.name => mapping_for_attribute(attribute)
31
+ end
32
+ result
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module Gummi
2
+ module DbLayer
3
+ module Document
4
+ module Object
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Virtus.model
9
+ include Gummi::DbLayer::Document::Attributes
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ module Gummi
2
+ module DbLayer
3
+ module Document
4
+ module Search
5
+ class Filtered
6
+ include Gummi::DbLayer::Document::Search::Searching
7
+
8
+ attribute :query_string, Gummi::DbLayer::Fields::SanitizedString
9
+ attribute :query_filters, Array[Hash], default: []
10
+ attribute :facets, Hash, default: {}
11
+
12
+ def to_client_args
13
+ args = super
14
+ args[:body] = { query: filtered, facets: facets }
15
+ args
16
+ end
17
+
18
+ def query
19
+ if query_string.present?
20
+ {query_string: { query: query_string}}
21
+ else
22
+ {match_all: {}}
23
+ end
24
+ end
25
+
26
+ def filtered
27
+ { 'filtered' => { 'query' => query, 'filter' => process_query_filters }}
28
+ end
29
+
30
+ def process_query_filters
31
+ if query_filters.length > 1
32
+ {and: query_filters}
33
+ else
34
+ query_filters.first
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ module Gummi
2
+ module DbLayer
3
+ module Document
4
+ module Search
5
+ class Raw
6
+ include Gummi::DbLayer::Document::Search::Searching
7
+
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end