elasticsearch-model-globalize 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e1ca50ca38311f893fa8b213704174c16a2facc3
4
+ data.tar.gz: 069ef0416c36d6bedc84b0702d8fe096bdd7524a
5
+ SHA512:
6
+ metadata.gz: f23d21df326bf57c9ef150e4e73f13a4703bf137f572441083f964ee7dc2f7ede3a30c3dce9af50289324b1343990fd078e9957b01cde0b683d1c0bc2dcde0d0
7
+ data.tar.gz: 31876715ab920f987f01847eb028dd592b44619a40df017daae1d520d07b378aa3011a1432918a2caca8d8aaa349ed2b917dc113bda89042e3a91a06f57341dc
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+
24
+ /vendor
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elasticsearch-model-globalize.gemspec
4
+ gemspec
5
+
6
+ gem 'pry-byebug', github: 'lencioni/pry-byebug', branch: '3.0.0'
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Kentaro Imai
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # Elasticsearch::Model::Globalize
2
+
3
+ `elasticsearch-model-globalize` library allows you to use `elasticsearch-model` library
4
+ with `Globalize` gem.
5
+
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'elasticsearch-model-globalize'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install elasticsearch-model-globalize
20
+
21
+
22
+ ## Usage
23
+
24
+ There are a few ways to use `elasticsearch-model` with `Globalize`.
25
+
26
+ This gem has two modules, one is `MultipleFields` and the other is `OneIndexPerLanguage`.
27
+ You can choose one for each model to fit your needs by simply including a module.
28
+
29
+ ```ruby
30
+ class Item < ActiveRecord::Base
31
+ include Elasticsearch::Model
32
+ include Elasticsearch::Model::Callbacks
33
+ include Elasticsearch::Model::Globalize::MultipleFields
34
+ # Or
35
+ # include Elasticsearch::Model::Globalize::OneIndexPerLanguage
36
+ end
37
+ ```
38
+
39
+
40
+ ### MultipleFields
41
+
42
+ `MultipleFields` module creates additional fields for each language.
43
+ For example, you have a model like:
44
+
45
+ ```ruby
46
+ class Item < ActiveRecord::Base
47
+ translates :name, :description
48
+ include Elasticsearch::Model
49
+ include Elasticsearch::Model::Callbacks
50
+ include Elasticsearch::Model::Globalize::MultipleFields
51
+
52
+ mapping do
53
+ indexes :id, type: 'integer'
54
+ indexes :name_ja, analyzer: 'kuromoji'
55
+ indexes :name_en, analyzer: 'snowball'
56
+ indexes :description_ja, analyzer: 'kuromoji'
57
+ indexes :description_en, analyzer: 'snowball'
58
+ end
59
+ end
60
+ ```
61
+
62
+ and you have `:en` and `:ja` for available locales.
63
+
64
+ `MultipleFields` module creates `name_en`, `name_ja`, `description_en` and `description_ja`
65
+ field and stores these fields instead of `name` and `description` fields into Elasticsearch.
66
+
67
+ You can customize the way to localize field names.
68
+
69
+ ```ruby
70
+ # Put this in config/initializers/elasticsearch-model-globalize.rb
71
+ Elasticsearch::Model::Globalize::MultipleFields.localized_name do |name, locale|
72
+ "#{locale}_#{name}"
73
+ end
74
+ ```
75
+
76
+ One thing you have to care about is that put `translates` line before includeing
77
+ `Elasticsearch::Model::Globalize::MultipleFields`, otherwise `MultipleFields` module are not able to
78
+ know from which fields to derive localized fields.
79
+
80
+
81
+ ### OneIndexPerLanguage
82
+
83
+ `OneIndexPerLanguage` module creates one index per language.
84
+ For example, you have a model like:
85
+
86
+ ```ruby
87
+ class Item < ActiveRecord::Base
88
+ translates :name, :description
89
+ include Elasticsearch::Model
90
+ include Elasticsearch::Model::Callbacks
91
+ include Elasticsearch::Model::Globalize::OneIndexPerLanguage
92
+ end
93
+ ```
94
+
95
+ and you have `:en` and `:ja` for available locales,
96
+
97
+ `OneIndexPerLanguage` module creates `items-en` and `items-ja` indices.
98
+
99
+ You can customize the way to localize index names and document types.
100
+
101
+ ```ruby
102
+ # Put this in config/initializers/elasticsearch-model-globalize.rb
103
+ Elasticsearch::Model::Globalize::OneIndexPerLanguage.localized_name do |name, locale|
104
+ "#{locale}-#{name}"
105
+ end
106
+ ```
107
+
108
+ You can use `index_name_base` and `documenty_type_base` in addition to `index_name` and
109
+ `document_type`.
110
+
111
+
112
+ You can define mappings using `globalized_maping` as follows:
113
+
114
+ ```ruby
115
+ class Item < ActiveRecord::Base
116
+ translates :name, :description
117
+ include Elasticsearch::Model
118
+ include Elasticsearch::Model::Callbacks
119
+ include Elasticsearch::Model::OneIndexPerLanguage
120
+
121
+ globalized_mapping do |locale|
122
+ analyzer = locale == :ja ? 'kuromoji' : 'snowball'
123
+
124
+ indexes :id, type: 'integer'
125
+ indexes :name, analyzer: analyzer
126
+ indexes :description, analyzer: analyzer
127
+ end
128
+ end
129
+ ```
130
+
131
+
132
+ ## Development and Community
133
+
134
+ For local development, clone the repository and run bundle install.
135
+
136
+ To run all tests against a test Elasticsearch cluster, use a command like this:
137
+
138
+ ```sh
139
+ curl -# https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.2.1.tar.gz | tar xz -C tmp/
140
+ SERVER=start TEST_CLUSTER_COMMAND=$PWD/tmp/elasticsearch-1.2.1/bin/elasticsearch bundle exec rake test
141
+ ```
142
+
143
+
144
+ ## Contributing
145
+
146
+ 1. Fork it ( https://github.com/kentaroi/elasticsearch-model-globalize/fork )
147
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
148
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
149
+ 4. Push to the branch (`git push origin my-new-feature`)
150
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Goma'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elasticsearch/model/globalize/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "elasticsearch-model-globalize"
8
+ spec.version = Elasticsearch::Model::Globalize::VERSION
9
+ spec.authors = ["Kentaro Imai"]
10
+ spec.email = ["kentaroi@gmail.com"]
11
+ spec.summary = %q{A library for using elasticsearch-model with globalize}
12
+ spec.description = %q{A library for using elasticsearch-model with globalize}
13
+ spec.homepage = "https://github.com/kentaroi/elastic-search-model-globalize"
14
+ spec.license = "Apache 2"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'elasticsearch-model'
22
+ spec.add_dependency 'globalize'
23
+ spec.add_dependency 'activerecord'
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "minitest", "~> 4.0"
28
+ spec.add_development_dependency "shoulda-context"
29
+ spec.add_development_dependency "mocha"
30
+ spec.add_development_dependency "pry-alias"
31
+ spec.add_development_dependency "elasticsearch-extensions"
32
+ end
@@ -0,0 +1,10 @@
1
+ require "elasticsearch/model/globalize/version"
2
+
3
+ module Elasticsearch
4
+ module Model
5
+ module Globalize
6
+ autoload :MultipleFields, 'elasticsearch/model/globalize/multiple_fields'
7
+ autoload :OneIndexPerLanguage, 'elasticsearch/model/globalize/one_index_per_language'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,81 @@
1
+ module Elasticsearch
2
+ module Model
3
+ module Globalize
4
+ module MultipleFields
5
+ def self.included(base)
6
+ base.class_eval do
7
+ class << self
8
+ def locales
9
+ @locales ||= I18n.available_locales
10
+ end
11
+ end
12
+
13
+ locales.each do |locale|
14
+ translated_attribute_names.each do |name|
15
+ localized_name = MultipleFields.localized_name(name, locale)
16
+
17
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1 # Define getter
18
+ def #{localized_name}
19
+ globalize.stash.contains?(:#{locale}, '#{name}') ? globalize.stash[:#{locale}]['#{name}']
20
+ : translation_for(:#{locale}, false).try(:read_attribute, '#{name}')
21
+ end
22
+ METHOD
23
+
24
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1 # Define setter
25
+ def #{localized_name}=(val)
26
+ attribute_will_change!(:#{localized_name})
27
+ write_attribute('#{name}', val, locale: :#{locale})
28
+ end
29
+ METHOD
30
+ end
31
+ end
32
+
33
+ translated_attribute_names.each do |name|
34
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
35
+ def #{name}=(val)
36
+ attribute_will_change!(::Elasticsearch::Model::Globalize::MultipleFields.localized_name('#{name}', ::Globalize.locale))
37
+ write_attribute('#{name}', val)
38
+ end
39
+ METHOD
40
+ end
41
+
42
+ def __elasticsearch__ &block
43
+ unless @__elasticsearch__
44
+ @__elasticsearch__ = ::Elasticsearch::Model::Proxy::InstanceMethodsProxy.new(self)
45
+ @__elasticsearch__.extend Elasticsearch::Model::Globalize::MultipleFields::InstanceMethods
46
+ end
47
+ @__elasticsearch__.instance_eval(&block) if block_given?
48
+ @__elasticsearch__
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.localized_name(name=nil, locale=nil, &block)
54
+ @localizer = block if block_given?
55
+
56
+ @localizer ? @localizer.call(name, locale) : "#{name}_#{locale}"
57
+ end
58
+
59
+ module InstanceMethods
60
+ def as_globalized_json(options={})
61
+ h = self.as_json
62
+
63
+ translated_attribute_names.each do |name|
64
+ h.delete(name.to_s)
65
+
66
+ self.class.locales.each do |locale|
67
+ localized_name = Elasticsearch::Model::Globalize::MultipleFields.localized_name(name, locale)
68
+ h[localized_name] = send(localized_name)
69
+ end
70
+ end
71
+ h
72
+ end
73
+
74
+ def as_indexed_json(options={})
75
+ self.as_globalized_json(options.merge root: false)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,235 @@
1
+ module Elasticsearch
2
+ module Model
3
+ module Globalize
4
+ module OneIndexPerLanguage
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ self.__elasticsearch__.class_eval do
9
+ include Elasticsearch::Model::Globalize::OneIndexPerLanguage::ClassMethods
10
+ end
11
+
12
+ class << self
13
+ [:globalized_mapping, :globalized_mappings, :import_globally, :index_name_base, :index_name_base=, :document_type_base, :document_type_base=].each do |m|
14
+ delegate m, to: :__elasticsearch__
15
+ end
16
+ end
17
+
18
+ def __elasticsearch__ &block
19
+ unless @__elasticsearch__
20
+ @__elasticsearch__ = ::Elasticsearch::Model::Proxy::InstanceMethodsProxy.new(self)
21
+ @__elasticsearch__.extend Elasticsearch::Model::Globalize::OneIndexPerLanguage::InstanceMethods
22
+ end
23
+ @__elasticsearch__.instance_eval(&block) if block_given?
24
+ @__elasticsearch__
25
+ end
26
+
27
+ before_update :update_existence_of_documents
28
+ before_save :update_changed_attributes_by_locale
29
+ end
30
+ end
31
+
32
+ def self.localized_name(name=nil, locale=nil, &block)
33
+ @localizer = block if block_given?
34
+
35
+ @localizer ? @localizer.call(name, locale) : "#{name}-#{locale}"
36
+ end
37
+
38
+ module ClassMethods
39
+ def index_name_base name=nil
40
+ @index_name_base = name and index_names.clear if name
41
+ @index_name_base || self.model_name.collection.gsub(/\//, '-')
42
+ end
43
+
44
+ def index_name_base=(name)
45
+ index_names.clear
46
+ @index_name_base = name
47
+ end
48
+
49
+ def document_type_base name=nil
50
+ @document_type_base = name and document_types.clear if name
51
+ @document_type_base || self.model_name.element
52
+ end
53
+
54
+ def document_type_base=(name)
55
+ document_types.clear
56
+ @document_type_base = name
57
+ end
58
+
59
+ def index_name name=nil
60
+ @index_name = name if name
61
+ @index_name || index_names[::Globalize.locale] ||=
62
+ ::Elasticsearch::Model::Globalize::OneIndexPerLanguage.localized_name(index_name_base, ::Globalize.locale)
63
+ end
64
+
65
+ def index_names
66
+ @index_names ||= {}
67
+ end
68
+
69
+ def document_type name=nil
70
+ @document_type = name if name
71
+ @document_type || document_types[::Globalize.locale] ||=
72
+ ::Elasticsearch::Model::Globalize::OneIndexPerLanguage.localized_name(document_type_base, ::Globalize.locale)
73
+ end
74
+
75
+ def document_types
76
+ @document_types ||= {}
77
+ end
78
+
79
+ def globalized_mapping(options={}, &block)
80
+ unless @globalized_mapping
81
+ @globalized_mapping = ActiveSupport::HashWithIndifferentAccess.new
82
+ target.__elasticsearch__.class_eval <<-RUBY, __FILE__, __LINE__ + 1
83
+ def mapping
84
+ @globalized_mapping[::Globalize.locale]
85
+ end
86
+ alias :mappings :mapping
87
+ RUBY
88
+ end
89
+ I18n.available_locales.each do |locale|
90
+ @globalized_mapping[locale] ||= ::Elasticsearch::Model::Indexing::Mappings.new(
91
+ ::Elasticsearch::Model::Globalize::OneIndexPerLanguage.localized_name(document_type_base, locale), options)
92
+
93
+ if block_given?
94
+ @globalized_mapping[locale].options.update(options)
95
+
96
+ @globalized_mapping[locale].instance_exec(locale, &block)
97
+ end
98
+ end
99
+ @globalized_mapping
100
+ end
101
+ alias :globalized_mappings :globalized_mapping
102
+
103
+ def import(options={}, &block)
104
+ current_locale_only = options.delete(:current_locale_only)
105
+ if current_locale_only
106
+ super(options, &block)
107
+ else
108
+ errors = Hash.new
109
+ I18n.available_locales.each do |locale|
110
+ ::Globalize.with_locale(locale) do
111
+ errors[locale] = super(options, &block)
112
+ end
113
+ end
114
+ self.find_each do |record|
115
+ (I18n.available_locales - record.translations.pluck(:locale).map(&:to_sym)).each do |locale|
116
+ ::Globalize.with_locale(locale) do
117
+ record.__elasticsearch__.delete_document(current_locale_only: true)
118
+ end
119
+ end
120
+ end
121
+ errors
122
+ end
123
+ end
124
+ end
125
+
126
+ module InstanceMethods
127
+ attr_accessor :existence_of_documents, :changed_attributes_by_locale
128
+
129
+ def changed_attributes_by_locale
130
+ @changed_attributes_by_locale ||= ActiveSupport::HashWithIndifferentAccess.new
131
+ end
132
+
133
+ def existence_of_documents
134
+ @existence_of_documents ||= ActiveSupport::HashWithIndifferentAccess.new
135
+ end
136
+
137
+ def index_name_base name=nil
138
+ @index_name_base = name || @index_name_base || self.class.index_name_base
139
+ end
140
+
141
+ def index_name_base=(name)
142
+ @index_name_base = name
143
+ end
144
+
145
+ def document_type_base name=nil
146
+ @document_type_base = name || @document_type_base || self.class.document_type_base
147
+ end
148
+
149
+ def document_type_base=(name)
150
+ @document_type_base = name
151
+ end
152
+
153
+ def index_name
154
+ self.class.index_name
155
+ end
156
+
157
+ def document_type
158
+ self.class.document_type
159
+ end
160
+
161
+ def index_document(options={})
162
+ current_locale_only = options.delete(:current_locale_only)
163
+ if current_locale_only
164
+ super(options)
165
+ else
166
+ changed_attributes_by_locale.keys.each do |locale|
167
+ ::Globalize.with_locale(locale) do
168
+ super(options)
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ def update_document(options={})
175
+ changed_attributes_by_locale.keys.each do |locale|
176
+ ::Globalize.with_locale(locale) do
177
+ if existence_of_documents[locale]
178
+ attributes = if respond_to?(:as_indexed_json)
179
+ changed_attributes_by_locale[locale].select{ |k, v| as_indexed_json.keys.include? k }
180
+ else
181
+ changed_attributes_by_locale[locale]
182
+ end
183
+
184
+ client.update(
185
+ { index: index_name,
186
+ type: document_type,
187
+ id: self.id,
188
+ body: { doc: attributes } }.merge(options)
189
+ )
190
+ else
191
+ index_document(current_locale_only: true)
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ def delete_document(options={})
198
+ current_locale_only = options.delete(:current_locale_only)
199
+ if current_locale_only
200
+ super(options)
201
+ else
202
+ translations.pluck(:locale).each do |locale|
203
+ ::Globalize.with_locale(locale) do
204
+ super(options)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # This method actually checks existence of translations in database
212
+ # Therefore, database and elasticsearch must be synced.
213
+ def update_existence_of_documents
214
+ __elasticsearch__.existence_of_documents.clear
215
+ translations.each do |t|
216
+ __elasticsearch__.existence_of_documents[t.locale] = true if t.persisted?
217
+ end
218
+ true
219
+ end
220
+
221
+ def update_changed_attributes_by_locale
222
+ __elasticsearch__.changed_attributes_by_locale.clear
223
+
224
+ common_changed_attributes = Hash[ changes.map{ |key, value| [key, value.last] } ]
225
+ translated_attribute_names.each { |k| common_changed_attributes.delete(k) }
226
+
227
+ globalize.stash.reject{ |locale, attrs| attrs.empty? }.each do |locale, attrs|
228
+ __elasticsearch__.changed_attributes_by_locale[locale] = attrs.merge(common_changed_attributes)
229
+ end
230
+ true
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,7 @@
1
+ module Elasticsearch
2
+ module Model
3
+ module Globalize
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+ require 'elasticsearch/model/globalize/multiple_fields'
3
+
4
+ class MultipleFieldsIntegrationTest < Elasticsearch::Test::IntegrationTestCase
5
+
6
+ class Article < ActiveRecord::Base
7
+ translates :title
8
+ @locales = [:en, :ja]
9
+ include Elasticsearch::Model
10
+ include Elasticsearch::Model::Callbacks
11
+ include Elasticsearch::Model::Globalize::MultipleFields
12
+
13
+ settings index: {number_of_shards: 1, number_of_replicas: 1} do
14
+ mapping do
15
+ indexes :title_en, analyzer: 'snowball'
16
+ indexes :title_ja, analyzer: 'cjk'
17
+ end
18
+ end
19
+ end
20
+
21
+ context "Mulfield module" do
22
+ setup do
23
+ ActiveRecord::Schema.define(version: 1) do
24
+ create_table :articles do |t|
25
+ t.string :title
26
+ end
27
+ Article.create_translation_table! title: :string
28
+ end
29
+ Article.destroy_all
30
+ Article.__elasticsearch__.create_index! force: true
31
+ a = Article.new
32
+ a.title = 'Search engine'
33
+ Globalize.with_locale(:ja) { a.title = '検索エンジン' }
34
+ a.save!
35
+ Globalize.with_locale(:ja) { Article.create! title: '世界貿易機関 World Trade Organization' }
36
+ Article.create! title: 'Testing code'
37
+ Article.__elasticsearch__.refresh_index!
38
+ end
39
+
40
+ should 'search' do
41
+ assert_equal 1, Article.search('search').results.total
42
+ assert_equal 1, Article.search('検索').results.total
43
+ end
44
+
45
+ should 'mapping' do
46
+ assert_equal 1, Article.search('title_en:testing').results.total
47
+ assert_equal 1, Article.search('title_en:test').results.total, 'Should be snowball analyzer'
48
+ assert_equal 1, Article.search('title_ja:organization').results.total
49
+ assert_equal 0, Article.search('title_ja:organ').results.total, 'Should not be snowball analyzer'
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+ require 'elasticsearch/model/globalize/one_index_per_language'
3
+
4
+ class OneIndexPerLanguageIntegrationTest < Elasticsearch::Test::IntegrationTestCase
5
+
6
+ class Article < ActiveRecord::Base
7
+ translates :title
8
+ include Elasticsearch::Model
9
+ include Elasticsearch::Model::Callbacks
10
+ include Elasticsearch::Model::Globalize::OneIndexPerLanguage
11
+
12
+ settings index: {number_of_shards: 1, number_of_replicas: 1} do
13
+ globalized_mapping do |locale|
14
+ if locale == :en
15
+ indexes :title, analyzer: 'snowball'
16
+ else
17
+ indexes :title, analyzer: 'cjk'
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ context "OneIndexPerLanguage module" do
24
+ setup do
25
+ ActiveRecord::Schema.define(version: 1) do
26
+ create_table :articles do |t|
27
+ t.string :title
28
+ end
29
+ Article.create_translation_table! title: :string
30
+ end
31
+ Article.destroy_all
32
+ Article.__elasticsearch__.create_index! force: true
33
+ a = Article.new
34
+ a.title = 'Search engine'
35
+ Globalize.with_locale(:ja) { a.title = '検索エンジン' }
36
+ a.save!
37
+ Globalize.with_locale(:ja) { Article.create! title: '世界貿易機関 World Trade Organization' }
38
+ Article.create! title: 'Testing code'
39
+ Article.__elasticsearch__.refresh_index!
40
+ sleep 1
41
+ end
42
+
43
+ should 'search' do
44
+ Globalize.with_locale(:en) do
45
+ assert_equal 1, Article.search('search').results.total
46
+ assert_equal 0, Article.search('検索').results.total
47
+ end
48
+
49
+ Globalize.with_locale(:ja) do
50
+ assert_equal 0, Article.search('search').results.total
51
+ assert_equal 1, Article.search('検索').results.total
52
+ end
53
+ end
54
+
55
+ should 'mapping' do
56
+ Globalize.with_locale(:en) do
57
+ assert_equal 1, Article.search('title:testing').results.total
58
+ assert_equal 1, Article.search('title:test').results.total, 'Should be snowball analyzer'
59
+ end
60
+
61
+ Globalize.with_locale(:ja) do
62
+ assert_equal 1, Article.search('title:organization').results.total
63
+ assert_equal 0, Article.search('title:organ').results.total, 'Should not be snowball analyzer'
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,56 @@
1
+ RUBY_1_8 = defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
2
+
3
+ exit(0) if RUBY_1_8
4
+
5
+ # Register `at_exit` handler for integration tests shutdown
6
+ # MUST be called before requiring 'test/unit'
7
+ if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
8
+ at_exit { Elasticsearch::Test::IntegrationTestCase.__run_at_exit_hooks }
9
+ end
10
+
11
+ require 'test/unit'
12
+ require 'shoulda-context'
13
+ require 'mocha/setup'
14
+ require 'pry-byebug'
15
+ require 'pry-alias'
16
+
17
+ require 'active_record'
18
+ require 'active_model'
19
+
20
+ require 'elasticsearch/model'
21
+
22
+ require 'elasticsearch/extensions/test/cluster'
23
+ require 'elasticsearch/extensions/test/startup_shutdown'
24
+
25
+ require 'globalize'
26
+
27
+
28
+ module Elasticsearch
29
+ module Test
30
+ class IntegrationTestCase < ::Test::Unit::TestCase
31
+ extend Elasticsearch::Extensions::Test::StartupShutdown
32
+
33
+ startup { Elasticsearch::Extensions::Test::Cluster.start(nodes: 1) if ENV['SERVER'] and not Elasticsearch::Extensions::Test::Cluster.running? }
34
+ shutdown { Elasticsearch::Extensions::Test::Cluster.stop if ENV['SERVER'] && started? }
35
+
36
+ def setup
37
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
38
+ logger = ::Logger.new(STDERR)
39
+ logger.formatter = lambda { |s, d, p, m| "#{m.ansi(:faint, :cyan)}\n" }
40
+ ActiveRecord::Base.logger = logger unless ENV['QUIET']
41
+
42
+ ActiveRecord::LogSubscriber.colorize_logging = false
43
+ ActiveRecord::Migration.verbose = false
44
+
45
+ tracer = ::Logger.new(STDERR)
46
+ tracer.formatter = lambda { |s, d, p, m| "#{m.gsub(/^.*$/) { |n| ' ' + n }.ansi(:faint)}\n" }
47
+
48
+ Elasticsearch::Model.client = Elasticsearch::Client.new host: "localhost:#{(ENV['TEST_CLUSTER_PORT'] || 9250)}",
49
+ tracer: (ENV['QUIET'] ? nil : tracer)
50
+
51
+ I18n.enforce_available_locales = true
52
+ I18n.available_locales = [:en, :ja]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,126 @@
1
+ require 'test_helper'
2
+ require 'elasticsearch/model/globalize/multiple_fields'
3
+
4
+ class MultipleFieldsTest < Test::Unit::TestCase
5
+ class Article < ActiveRecord::Base
6
+ translates :title, :body
7
+ @locales = [:en, :ja]
8
+ include Elasticsearch::Model
9
+ include Elasticsearch::Model::Callbacks
10
+ include Elasticsearch::Model::Globalize::MultipleFields
11
+ end
12
+
13
+ def setup
14
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
15
+ logger = ::Logger.new(STDERR)
16
+ logger.formatter = lambda { |s, d, p, m| "#{m.ansi(:faint, :cyan)}\n" }
17
+ ActiveRecord::Base.logger = logger unless ENV['QUIET']
18
+
19
+ ActiveRecord::LogSubscriber.colorize_logging = false
20
+ ActiveRecord::Migration.verbose = false
21
+
22
+ tracer = ::Logger.new(STDERR)
23
+ tracer.formatter = lambda { |s, d, p, m| "#{m.gsub(/^.*$/) { |n| ' ' + n }.ansi(:faint)}\n" }
24
+
25
+
26
+ ActiveRecord::Schema.define(version: 1) do
27
+ create_table :articles do |t|
28
+ t.string :title
29
+ t.text :body
30
+ t.string :code
31
+ end
32
+ Article.create_translation_table! title: :string, body: :text
33
+ end
34
+ I18n.enforce_available_locales = true
35
+ I18n.available_locales = [:en, :ja]
36
+ end
37
+
38
+ def test_multiple_fields_attribute
39
+ a = Article.new
40
+ assert_nil a.title
41
+ a.title = 'English'
42
+ assert_equal 'English', a.title
43
+ assert_equal 'English', a.title_en
44
+ assert_nil a.title_ja
45
+ assert_equal 2, a.changes.keys.count
46
+ assert a.changes.has_key?(:title_en)
47
+ assert a.changes.has_key?(:title)
48
+
49
+ Globalize.with_locale(:ja) {
50
+ assert_nil a.title
51
+ a.title = '日本語'
52
+ assert_equal '日本語', a.title
53
+ assert_equal '日本語', a.title_ja
54
+ assert_equal 'English', a.title_en
55
+ assert_equal 3, a.changes.keys.count
56
+ assert a.changes.has_key?(:title_en)
57
+ assert a.changes.has_key?(:title_ja)
58
+ assert a.changes.has_key?(:title)
59
+ }
60
+ assert_equal 'English', a.title
61
+ assert_equal 'English', a.title_en
62
+ assert_equal '日本語', a.title_ja
63
+
64
+ a.title_en = 'English title'
65
+ assert_equal 'English title', a.title_en
66
+ a.title_ja = '日本語タイトル'
67
+ assert_equal '日本語タイトル', a.title_ja
68
+ assert_equal 'English title', a.title
69
+ assert_equal '日本語タイトル', Globalize.with_locale(:ja){ a.title }
70
+ end
71
+
72
+ def test_create
73
+ a = Article.new
74
+ a.title = 'title'
75
+ assert_equal 'title', a.title_en
76
+ a.__elasticsearch__.client.expects(:index).with { |value|
77
+ value[:index] == 'multiple_fields_test-articles' &&
78
+ value[:type] == 'article' &&
79
+ value[:body].count == 6 &&
80
+ value[:body].has_key?('id') &&
81
+ value[:body]['title_en'] == 'title' &&
82
+ value[:body]['title_ja'] == nil &&
83
+ value[:body]['body_en'] == nil &&
84
+ value[:body]['body_ja'] == nil &&
85
+ value[:body]['code'] == nil
86
+ }
87
+ a.save
88
+ end
89
+
90
+ def test_update
91
+ a = Article.new
92
+ a.__elasticsearch__.stubs(:index_document)
93
+ a.title = 'title'
94
+ a.body = 'body'
95
+ a.save
96
+ a.title = 'new title'
97
+ a.__elasticsearch__.client.expects(:update).with { |value|
98
+ value[:index] == 'multiple_fields_test-articles' &&
99
+ value[:type] == 'article' &&
100
+ value[:id] == a.id &&
101
+ value[:body][:doc].count == 1 &&
102
+ value[:body][:doc]['title_en'] == 'new title'
103
+ }
104
+ a.save
105
+ end
106
+
107
+ def test_update_with_no_translation_field
108
+ a = Article.new
109
+ a.__elasticsearch__.stubs(:index_document)
110
+ a.title = 'title'
111
+ a.body = 'body'
112
+ a.code = 'code'
113
+ a.save
114
+ a.title = 'new title'
115
+ a.code = 'no derived fields'
116
+ a.__elasticsearch__.client.expects(:update).with { |value|
117
+ value[:index] == 'multiple_fields_test-articles' &&
118
+ value[:type] == 'article' &&
119
+ value[:id] == a.id &&
120
+ value[:body][:doc].count == 2 &&
121
+ value[:body][:doc]['title_en'] == 'new title'
122
+ value[:body][:doc]['code'] == 'no derived fields'
123
+ }
124
+ a.save
125
+ end
126
+ end
@@ -0,0 +1,137 @@
1
+ require 'test_helper'
2
+ require 'elasticsearch/model/globalize/one_index_per_language'
3
+
4
+ class OneIndexPerLanguageTest < Test::Unit::TestCase
5
+ class Article < ActiveRecord::Base
6
+ translates :title, :body
7
+ @locales = [:en, :ja]
8
+ include Elasticsearch::Model
9
+ include Elasticsearch::Model::Callbacks
10
+ include Elasticsearch::Model::Globalize::OneIndexPerLanguage
11
+ end
12
+
13
+ def setup
14
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
15
+ logger = ::Logger.new(STDERR)
16
+ logger.formatter = lambda { |s, d, p, m| "#{m.ansi(:faint, :cyan)}\n" }
17
+ ActiveRecord::Base.logger = logger unless ENV['QUIET']
18
+
19
+ ActiveRecord::LogSubscriber.colorize_logging = false
20
+ ActiveRecord::Migration.verbose = false
21
+
22
+ tracer = ::Logger.new(STDERR)
23
+ tracer.formatter = lambda { |s, d, p, m| "#{m.gsub(/^.*$/) { |n| ' ' + n }.ansi(:faint)}\n" }
24
+
25
+
26
+ ActiveRecord::Schema.define(version: 1) do
27
+ create_table :articles do |t|
28
+ t.string :title
29
+ t.text :body
30
+ t.string :code
31
+ end
32
+ Article.create_translation_table! title: :string, body: :text
33
+ end
34
+ I18n.enforce_available_locales = true
35
+ I18n.available_locales = [:en, :ja]
36
+ end
37
+
38
+ def test_create
39
+ a = Article.new
40
+ a.title = 'title'
41
+ a.__elasticsearch__.client.expects(:index).with { |value|
42
+ value[:index] == 'one_index_per_language_test-articles-en' &&
43
+ value[:type] == 'article-en' &&
44
+ value[:body].count == 4 &&
45
+ value[:body].has_key?('id') &&
46
+ value[:body]['title'] == 'title' &&
47
+ value[:body]['body'] == nil &&
48
+ value[:body]['code'] == nil
49
+ }
50
+ a.save
51
+ end
52
+
53
+ def test_create_multiple_locales
54
+ a = Article.new
55
+ a.title = 'title'
56
+ Globalize.with_locale(:ja) { a.title = 'タイトル' }
57
+ a.__elasticsearch__.client.expects(:index).with { |value|
58
+ value[:index] == 'one_index_per_language_test-articles-en' &&
59
+ value[:type] == 'article-en' &&
60
+ value[:body].count == 4 &&
61
+ value[:body].has_key?('id') &&
62
+ value[:body]['title'] == 'title' &&
63
+ value[:body]['body'] == nil &&
64
+ value[:body]['code'] == nil
65
+ }
66
+ a.__elasticsearch__.client.expects(:index).with { |value|
67
+ value[:index] == 'one_index_per_language_test-articles-ja' &&
68
+ value[:type] == 'article-ja' &&
69
+ value[:body].count == 4 &&
70
+ value[:body].has_key?('id') &&
71
+ value[:body]['title'] == 'タイトル' &&
72
+ value[:body]['body'] == nil &&
73
+ value[:body]['code'] == nil
74
+ }
75
+ a.save
76
+ end
77
+
78
+ def test_create_with_no_translation_field
79
+ a = Article.new
80
+ a.title = 'title'
81
+ a.code = 'code'
82
+ a.__elasticsearch__.client.expects(:index).with { |value|
83
+ value[:index] == 'one_index_per_language_test-articles-en' &&
84
+ value[:type] == 'article-en' &&
85
+ value[:body].count == 4 &&
86
+ value[:body].has_key?('id') &&
87
+ value[:body]['title'] == 'title' &&
88
+ value[:body]['body'] == nil &&
89
+ value[:body]['code'] == 'code'
90
+ }
91
+ a.save
92
+ end
93
+
94
+ def test_update
95
+ a = Article.new
96
+ a.__elasticsearch__.stubs(:index_document)
97
+ a.title = 'title'
98
+ a.body = 'body'
99
+ a.save
100
+ a.title = 'new title'
101
+ a.__elasticsearch__.client.expects(:update).with { |value|
102
+ value[:index] == 'one_index_per_language_test-articles-en' &&
103
+ value[:type] == 'article-en' &&
104
+ value[:id] == a.id &&
105
+ value[:body][:doc].count == 1 &&
106
+ value[:body][:doc]['title'] == 'new title'
107
+ }
108
+ a.save
109
+ end
110
+
111
+ def test_update_with_new_locale
112
+ a = Article.new
113
+ a.__elasticsearch__.client.stubs(:index)
114
+ a.title = 'title'
115
+ a.body = 'body'
116
+ a.save
117
+ a.title = 'new title'
118
+ Globalize.with_locale(:ja) { a.title = 'タイトル' }
119
+ a.__elasticsearch__.client.expects(:update).with { |value|
120
+ value[:index] == 'one_index_per_language_test-articles-en' &&
121
+ value[:type] == 'article-en' &&
122
+ value[:id] == a.id &&
123
+ value[:body][:doc].count == 1 &&
124
+ value[:body][:doc]['title'] == 'new title'
125
+ }
126
+ a.__elasticsearch__.client.expects(:index).with { |value|
127
+ value[:index] == 'one_index_per_language_test-articles-ja' &&
128
+ value[:type] == 'article-ja' &&
129
+ value[:body].count == 4 &&
130
+ value[:body].has_key?('id') &&
131
+ value[:body]['title'] == 'タイトル' &&
132
+ value[:body]['body'] == nil &&
133
+ value[:body]['code'] == nil
134
+ }
135
+ a.save
136
+ end
137
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch-model-globalize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kentaro Imai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: elasticsearch-model
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: globalize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: shoulda-context
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: mocha
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry-alias
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: elasticsearch-extensions
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: A library for using elasticsearch-model with globalize
168
+ email:
169
+ - kentaroi@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".gitignore"
175
+ - Gemfile
176
+ - LICENSE.txt
177
+ - README.md
178
+ - Rakefile
179
+ - elasticsearch-model-globalize.gemspec
180
+ - lib/elasticsearch/model/globalize.rb
181
+ - lib/elasticsearch/model/globalize/multiple_fields.rb
182
+ - lib/elasticsearch/model/globalize/one_index_per_language.rb
183
+ - lib/elasticsearch/model/globalize/version.rb
184
+ - test/integration/multiple_fields_integration_test.rb
185
+ - test/integration/one_index_per_language_integration_test.rb
186
+ - test/test_helper.rb
187
+ - test/unit/multiple_fields_test.rb
188
+ - test/unit/one_index_per_language_test.rb
189
+ homepage: https://github.com/kentaroi/elastic-search-model-globalize
190
+ licenses:
191
+ - Apache 2
192
+ metadata: {}
193
+ post_install_message:
194
+ rdoc_options: []
195
+ require_paths:
196
+ - lib
197
+ required_ruby_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ required_rubygems_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ requirements: []
208
+ rubyforge_project:
209
+ rubygems_version: 2.2.2
210
+ signing_key:
211
+ specification_version: 4
212
+ summary: A library for using elasticsearch-model with globalize
213
+ test_files:
214
+ - test/integration/multiple_fields_integration_test.rb
215
+ - test/integration/one_index_per_language_integration_test.rb
216
+ - test/test_helper.rb
217
+ - test/unit/multiple_fields_test.rb
218
+ - test/unit/one_index_per_language_test.rb