activerecord_reindex 0.1.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.
- 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: []
|