activerecord_reindex 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/lib/activerecord_reindex.rb +18 -0
- data/lib/activerecord_reindex/adapter.rb +21 -0
- data/lib/activerecord_reindex/association.rb +86 -0
- data/lib/activerecord_reindex/association_reflection.rb +32 -0
- data/lib/activerecord_reindex/async_adapter.rb +43 -0
- data/lib/activerecord_reindex/base.rb +49 -0
- data/lib/activerecord_reindex/reindexer.rb +64 -0
- data/lib/activerecord_reindex/sync_adapter.rb +29 -0
- data/lib/activerecord_reindex/update_document_monkey_patch.rb +39 -0
- data/lib/activerecord_reindex/version.rb +5 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3eeb09396b487cf9fbd9d02ecb4f78ff27523665
|
4
|
+
data.tar.gz: 5b410be8a9139992259f0d87ff0c0d0bbb7db2f4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c63d9c04a190fe046eaca49262b6b327d6cb1f5108d1a6f1a78d8dfb13ce3ee77276b6ffae21b01232077cbe65a4671c896bd286719297d40fb6f09b14f1d241
|
7
|
+
data.tar.gz: 7359249c0e579f8889dd3d37c41b0e6eb1533fb451e5521fcb357ee924d1a3c1cdd560ec479aeb316376c73bcc3a54e5278ac609576812cf01e27d6bf7488d83
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Health24
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
require 'activerecord_reindex/version'
|
4
|
+
|
5
|
+
# monkey patch active record associations
|
6
|
+
require 'active_record'
|
7
|
+
require 'active_job'
|
8
|
+
|
9
|
+
require 'activerecord_reindex/base'
|
10
|
+
require 'activerecord_reindex/association'
|
11
|
+
require 'activerecord_reindex/association_reflection'
|
12
|
+
|
13
|
+
# monkey patch elasticsearch/model
|
14
|
+
require 'elasticsearch/model'
|
15
|
+
require 'activerecord_reindex/update_document_monkey_patch'
|
16
|
+
|
17
|
+
module ActiverecordReindex
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# Abstract adapter class
|
4
|
+
# New adapters should implement :call method that will perform reindexing of the given record
|
5
|
+
# Example:
|
6
|
+
# def call(record)
|
7
|
+
# reindex_it(record)
|
8
|
+
# end
|
9
|
+
module ActiverecordReindex
|
10
|
+
class Adapter
|
11
|
+
|
12
|
+
# check if record of this class can be reindexed
|
13
|
+
# check if klass inherits from elasticsearch-model base class
|
14
|
+
# and have method required for reindexing
|
15
|
+
def self._check_elasticsearch_connection(klass)
|
16
|
+
klass < Elasticsearch::Model
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
4
|
+
|
5
|
+
# Adds reindex option to associations
|
6
|
+
# values accepted are true, :async. Default false.
|
7
|
+
# If true it will add syncronous elasticsearch reindex callbacks on:
|
8
|
+
# 1. record updated
|
9
|
+
# 2. record destroyed
|
10
|
+
# 3. record index updated
|
11
|
+
# if :async it will add async callbacks in same cases
|
12
|
+
module ActiveRecord
|
13
|
+
module Associations
|
14
|
+
module Builder
|
15
|
+
class Association
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
alias original_valid_options valid_options
|
20
|
+
|
21
|
+
# This method monkey patches ActiveRecord valid_options to add one more valid option :reindex
|
22
|
+
# Examples:
|
23
|
+
# belongs_to :tag, reindex: true
|
24
|
+
# belongs_to :tagging, reindex: :async
|
25
|
+
# has_many :tags, reindex: async
|
26
|
+
# has_many :tags, through: :taggings, reindex: true
|
27
|
+
def valid_options(*args)
|
28
|
+
original_valid_options(*args) + [:reindex]
|
29
|
+
end
|
30
|
+
|
31
|
+
alias original_define_callbacks define_callbacks
|
32
|
+
|
33
|
+
# This method monkeypatches ActiveRecord define_callbacks to
|
34
|
+
# add reindex callbacks if corresponding option specified
|
35
|
+
# if reindex; true - add syncronous callback to reindex associated records
|
36
|
+
# if reindex: :async - add asyncronous callback to reindex associated records
|
37
|
+
def define_callbacks(model, reflection)
|
38
|
+
original_define_callbacks(model, reflection)
|
39
|
+
if reflection.reindex_sync?
|
40
|
+
add_reindex_callback(model, reflection, async: false)
|
41
|
+
model.sync_reindexable_reflections += [reflection]
|
42
|
+
elsif reflection.reindex_async?
|
43
|
+
add_reindex_callback(model, reflection, async: true)
|
44
|
+
model.async_reindexable_reflections += [reflection]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# manages adding of callbacks considering async option
|
51
|
+
def add_reindex_callback(model, reflection, async:)
|
52
|
+
add_destroy_reindex_callback(model, reflection, async: async)
|
53
|
+
|
54
|
+
add_update_reindex_callback(model, reflection, async: async)
|
55
|
+
end
|
56
|
+
|
57
|
+
# add callback to reindex associated records on destroy
|
58
|
+
# if association has dependent: :destroy or dependent: :delete_all
|
59
|
+
# we skip this callback since destroyed records should reindex themselves
|
60
|
+
def add_destroy_reindex_callback(model, reflection, async:)
|
61
|
+
return if [:destroy, :delete_all].include? reflection.options[:dependent]
|
62
|
+
|
63
|
+
model.after_commit on: :destroy, &callback(async, reflection)
|
64
|
+
end
|
65
|
+
|
66
|
+
# add callback to reindex associations on update
|
67
|
+
# if model inherited from Elasticsearch::Model it means it have own index in elasticsearch
|
68
|
+
# and therefore should reindex itself on update those triggering update_document hook
|
69
|
+
# to prevent double reindex we're not adding update callback on such models
|
70
|
+
def add_update_reindex_callback(model, reflection, async:)
|
71
|
+
return if model < Elasticsearch::Model
|
72
|
+
|
73
|
+
model.after_commit on: :update, &callback(async, reflection)
|
74
|
+
end
|
75
|
+
|
76
|
+
# callback methods defined in ActiveRecord::Base monkeypatch
|
77
|
+
def callback(async, reflection)
|
78
|
+
async ? -> { reindex_async(reflection) } : -> { reindex_sync(reflection) }
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
4
|
+
# Add helper methods to Activerecord Reflection
|
5
|
+
# for quick access to reindex options
|
6
|
+
module ActiveRecord
|
7
|
+
module Reflection
|
8
|
+
class AssociationReflection
|
9
|
+
|
10
|
+
def reindex_sync?
|
11
|
+
@options.fetch(:reindex, false) == true
|
12
|
+
end
|
13
|
+
|
14
|
+
def reindex_async?
|
15
|
+
@options.fetch(:reindex, false) == :async
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class ThroughReflection
|
21
|
+
|
22
|
+
def reindex_sync?
|
23
|
+
@delegate_reflection.options.fetch(:reindex, false) == true
|
24
|
+
end
|
25
|
+
|
26
|
+
def reindex_async?
|
27
|
+
@delegate_reflection.options.fetch(:reindex, false) == :async
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
|
4
|
+
# Asyncronouse reindex adapter
|
5
|
+
# uses Jobs for reindexing records asyncronously
|
6
|
+
# Using ActiveJob as dependency bcs activerecord is required for this so
|
7
|
+
# in most cases it would be used with rails hence with ActiveJob
|
8
|
+
# later can think about adding support for differnt job adapters
|
9
|
+
require_relative 'adapter'
|
10
|
+
module ActiverecordReindex
|
11
|
+
class AsyncAdapter < Adapter
|
12
|
+
|
13
|
+
# Job wrapper. Queues elastic_index queue for each reindex
|
14
|
+
class UpdateJob < ::ActiveJob::Base
|
15
|
+
|
16
|
+
# TODO: make queue name configurable
|
17
|
+
queue_as :elastic_index
|
18
|
+
|
19
|
+
def perform(klass, id, request_record_klass, request_record_id)
|
20
|
+
klass = klass.constantize
|
21
|
+
request_record = request_record_klass.constantize.find(request_record_id)
|
22
|
+
klass.find(id).__elasticsearch__.update_document(request_record: request_record)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
# ***nasty-stuff***
|
30
|
+
# hooking into update_document has sudden side-effect
|
31
|
+
# if associations defined two-way they will trigger reindex recursively and result in StackLevelTooDeep
|
32
|
+
# hence to prevent this we're passing request_record to adapter
|
33
|
+
# request record is record that initted reindex for current record as association
|
34
|
+
# we will skip it in associations reindex to prevent recursive reindex and StackLevelTooDeep error
|
35
|
+
def call(record, request_record)
|
36
|
+
return unless _check_elasticsearch_connection(record.class)
|
37
|
+
UpdateJob.perform_later(record.class, record.id, request_record.class, request_record.id)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
require_relative 'async_adapter'
|
4
|
+
require_relative 'sync_adapter'
|
5
|
+
require_relative 'reindexer'
|
6
|
+
|
7
|
+
# ActiveRecord::Base extension to provide methods for
|
8
|
+
# reindexing callbacks
|
9
|
+
# this methods requested in callbacks in association.rb
|
10
|
+
module ActiveRecord
|
11
|
+
class Base
|
12
|
+
|
13
|
+
def self.inherited(child)
|
14
|
+
super
|
15
|
+
class << child
|
16
|
+
|
17
|
+
attr_accessor :reindexer, :async_adapter, :sync_adapter, :sync_reindexable_reflections, :async_reindexable_reflections
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# Init default values to prevent undefined method for nilClass error
|
22
|
+
child.sync_reindexable_reflections = []
|
23
|
+
child.async_reindexable_reflections = []
|
24
|
+
|
25
|
+
child.reindexer = ActiverecordReindex::Reindexer.new
|
26
|
+
# TODO: provide config for changing adapters
|
27
|
+
# For now can set adapter through writers inside class
|
28
|
+
child.async_adapter = ActiverecordReindex::AsyncAdapter
|
29
|
+
child.sync_adapter = ActiverecordReindex::SyncAdapter
|
30
|
+
end
|
31
|
+
|
32
|
+
def reindex_async(reflection, skip_record: nil)
|
33
|
+
_reindex(reflection, strategy: self.class.async_adapter, skip_record: skip_record)
|
34
|
+
end
|
35
|
+
|
36
|
+
def reindex_sync(reflection, skip_record: nil)
|
37
|
+
_reindex(reflection, strategy: self.class.sync_adapter, skip_record: skip_record)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def _reindex(reflection, strategy:, skip_record:)
|
43
|
+
self.class.reindexer
|
44
|
+
.with_strategy(strategy)
|
45
|
+
.call(self, reflection: reflection, skip_record: skip_record)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
module ActiverecordReindex
|
4
|
+
class Reindexer
|
5
|
+
|
6
|
+
# chain strategy before actual executing
|
7
|
+
# strategy can be either sync or async
|
8
|
+
# corresponding to type of reindexing
|
9
|
+
# additional strategies can be defined and specified by user
|
10
|
+
def with_strategy(strategy)
|
11
|
+
@strategy = strategy
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
# reindex records associated with given record on given association
|
16
|
+
# if association is collection(has_many, has_many_through, has_and_belongs_to_many)
|
17
|
+
# get all associated recrods and reindex them
|
18
|
+
# else
|
19
|
+
# reindex given record associted one
|
20
|
+
def call(record, reflection:, skip_record:)
|
21
|
+
if reflection.collection?
|
22
|
+
_reindex_collection(reflection, record, skip_record)
|
23
|
+
else
|
24
|
+
associated_record = record.public_send(reflection.name)
|
25
|
+
return if associated_record == skip_record
|
26
|
+
_update_index(associated_record, record)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# TODO: add bulk reindex if need performance
|
33
|
+
# raise if strategy was not specified or doesn't respond to call which is required for strategy
|
34
|
+
# pass record to strategy and execute reindex
|
35
|
+
# clear strategy to not mess up future reindexing
|
36
|
+
def _update_index(associated_record, record)
|
37
|
+
_check_strategy
|
38
|
+
|
39
|
+
@strategy.call(associated_record, record)
|
40
|
+
|
41
|
+
_clear_strategy
|
42
|
+
end
|
43
|
+
|
44
|
+
def _check_strategy
|
45
|
+
raise ArgumentError, 'No strategy specified.' unless @strategy
|
46
|
+
raise ArgumentError, "Strategy specified incorrect. Check if #{@strategy} responds to :call." unless @strategy.respond_to? :call
|
47
|
+
end
|
48
|
+
|
49
|
+
def _clear_strategy
|
50
|
+
@strategy = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def _reindex_collection(reflection, record, skip_record)
|
54
|
+
collection = record.public_send(reflection.name)
|
55
|
+
|
56
|
+
collection -= [skip_record] if reflection.klass == skip_record.class
|
57
|
+
|
58
|
+
collection.each do |associated_record|
|
59
|
+
_update_index(associated_record, record)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# Reindexes records syncronously
|
4
|
+
require_relative 'adapter'
|
5
|
+
module ActiverecordReindex
|
6
|
+
class SyncAdapter < Adapter
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# updates index directly in elasticsearch through
|
11
|
+
# Elasticsearch::Model instance method
|
12
|
+
# if class not inherited from Elasticsearch::Model it skips since it cannot be reindexing
|
13
|
+
# TODO: show error\warning about trying to reindex record that is not connection to elastic
|
14
|
+
# ***nasty-stuff***
|
15
|
+
# hooking into update_document has sudden side-effect
|
16
|
+
# if associations defined two-way they will trigger reindex recursively and result in StackLevelTooDeep
|
17
|
+
# hence to prevent this we're passing request_record to adapter
|
18
|
+
# request record is record that initted reindex for current record as association
|
19
|
+
# we will skip it in associations reindex to prevent recursive reindex and StackLevelTooDeep error
|
20
|
+
def call(record, request_record)
|
21
|
+
return unless _check_elasticsearch_connection(record.class)
|
22
|
+
record.__elasticsearch__.update_document(request_record: request_record)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
module Elasticsearch
|
4
|
+
module Model
|
5
|
+
module Indexing
|
6
|
+
module InstanceMethods
|
7
|
+
|
8
|
+
alias original_update_document update_document
|
9
|
+
|
10
|
+
# monkey patch update_document method from elasticsearch gem
|
11
|
+
# use +super+ and hook on reindex to reindex associations
|
12
|
+
# for why request_record needed here and what it is see sync_adapter.rb
|
13
|
+
def update_document(*args, request_record: nil)
|
14
|
+
if _active_record_model?(self.class)
|
15
|
+
_reindex_reflections(self.class, request_record)
|
16
|
+
end
|
17
|
+
original_update_document(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def _active_record_model?(klass)
|
23
|
+
klass < ActiveRecord::Base
|
24
|
+
end
|
25
|
+
|
26
|
+
def _reindex_reflections(klass, request_record)
|
27
|
+
klass.sync_reindexable_reflections.each do |reflection|
|
28
|
+
target.reindex_sync(reflection, skip_record: request_record)
|
29
|
+
end
|
30
|
+
|
31
|
+
klass.async_reindexable_reflections.each do |reflection|
|
32
|
+
target.reindex_async(reflection, skip_record: request_record)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord_reindex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- vs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
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: activejob
|
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: elasticsearch-model
|
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
|
+
description:
|
56
|
+
email:
|
57
|
+
- vshaveyko@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- LICENSE
|
63
|
+
- lib/activerecord_reindex.rb
|
64
|
+
- lib/activerecord_reindex/adapter.rb
|
65
|
+
- lib/activerecord_reindex/association.rb
|
66
|
+
- lib/activerecord_reindex/association_reflection.rb
|
67
|
+
- lib/activerecord_reindex/async_adapter.rb
|
68
|
+
- lib/activerecord_reindex/base.rb
|
69
|
+
- lib/activerecord_reindex/reindexer.rb
|
70
|
+
- lib/activerecord_reindex/sync_adapter.rb
|
71
|
+
- lib/activerecord_reindex/update_document_monkey_patch.rb
|
72
|
+
- lib/activerecord_reindex/version.rb
|
73
|
+
homepage: https://github.com/Health24/activerecord_reindex
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.5.1
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Add Elasticsearch reindex option to ActiveRecord associations
|
97
|
+
test_files: []
|