search_and_resque 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ad86e4b35d185b5d25a7396261908351e938021
4
+ data.tar.gz: 3f50eb245144bb3a970d116797af9058633a363f
5
+ SHA512:
6
+ metadata.gz: fb86cffcdd4bd64c86a25d1785077fd82d4b35e04283d4323008b18d3d7960951a1a437a237a4f3a896dfe8c33cc17def5b6be3010b426c7f093f6fdf774f123
7
+ data.tar.gz: 8811816945c72859930ae4a043ca0fc510ad3887a43c642894480365182ea4b01ab2bab7ea078b64389a2beda8fa8d6c08c8184a6ded94e82285849926825e9c
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in search_and_resque.gemspec
4
+ gemspec
5
+
6
+ gem 'activerecord'
7
+ gem 'chewy'
8
+ gem 'resque'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Peter Woo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # SearchAndResque
2
+
3
+ ## Installation
4
+
5
+ Add these lines to your application's Gemfile:
6
+
7
+ gem 'chewy'
8
+ gem 'search_and_resque'
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install search_and_resque
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ class Film < ActiveRecord::Base
22
+ # Create after_save, after_destroy callbacks
23
+ search_and_resque :films
24
+ end
25
+
26
+ class Document < ActiveRecord::Base
27
+ # Create after_save, after_destroy callbacks, only run when document text has changed
28
+ search_and_resque :documents, :if => ->{ contents_changed? }
29
+ end
30
+
31
+ class MyIndex < Chewy::Index
32
+ define_type Film do
33
+ field :title
34
+ end
35
+
36
+ define_type Document do
37
+ field :contents
38
+ end
39
+ end
40
+ ```
41
+
42
+ Calling `search_and_resque` in a Rails model sets up `after_save`/`after_destroy` callbacks, which will enqueue Resque jobs to update (or delete from) the index.
43
+
44
+ ## Configuration
45
+
46
+ `SearchAndResque.index_name` must be set by the environment, as well as `SearchAndResque::Queue.queue` (the queue name).
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it ( https://github.com/[my-github-username]/search_and_resque/fork )
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,42 @@
1
+ require "search_and_resque/version"
2
+ require "search_and_resque/callbacks"
3
+ require "search_and_resque/queue"
4
+ require "search_and_resque/chewy_extensions"
5
+
6
+ module SearchAndResque
7
+ extend ActiveSupport::Concern
8
+
9
+ @queue = SearchAndResque::Queue
10
+
11
+ def self.queue=(queue)
12
+ @queue = queue
13
+ end
14
+
15
+ def self.queue
16
+ @queue
17
+ end
18
+
19
+ def self.index_name=(name)
20
+ @index_name = "#{name}"
21
+ end
22
+
23
+ def self.index_name
24
+ @index_name
25
+ end
26
+
27
+ module ClassMethods
28
+ def search_and_resque(type_name, options={})
29
+ unless included_modules.include?(SearchAndResque::Callbacks)
30
+ options[:if] ||= ->{ true }
31
+ define_method(:should_update_elastic_search?, &options[:if])
32
+
33
+ @elastic_search_type = "#{type_name}"
34
+
35
+ include SearchAndResque::Callbacks
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ActiveRecord::Base.send :include, SearchAndResque
42
+ Chewy::Type.send :include, SearchAndResque::ChewyExtensions::ChewyType
@@ -0,0 +1,69 @@
1
+ module SearchAndResque
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class << self
7
+ attr_accessor :elastic_search_type
8
+ end
9
+ after_save :enqueue_elastic_search_update
10
+ after_destroy :enqueue_elastic_search_delete
11
+ end
12
+
13
+ module ClassMethods
14
+ def enqueue_elastic_search_update(ids)
15
+ ids = Array(ids).map{ |x| x.is_a?(ActiveRecord::Base) ? x.id : x }
16
+ SearchAndResque.queue.enqueue_update(elastic_search_type, ids)
17
+ end
18
+
19
+ def enqueue_elastic_search_delete(ids)
20
+ ids = Array(ids).map{ |x| x.is_a?(ActiveRecord::Base) ? x.id : x }
21
+ SearchAndResque.queue.enqueue_delete(elastic_search_type, ids)
22
+ end
23
+ end
24
+
25
+ def enqueue_elastic_search_update
26
+ self.class.enqueue_elastic_search_update(id) if should_update_elastic_search?
27
+ end
28
+
29
+ def enqueue_elastic_search_delete
30
+ self.class.enqueue_elastic_search_delete(id)
31
+ end
32
+
33
+ # e.g.
34
+ # Model.will_update_all(@records) do
35
+ # ...
36
+ # @records.update_all(...)
37
+ # ...
38
+ # end
39
+ def will_update_all(ids)
40
+ begin
41
+ skip_callback(:save, :after, :enqueue_elastic_search_update)
42
+ transaction do
43
+ yield if block_given?
44
+ enqueue_elastic_search_update(ids) unless ids.empty?
45
+ end
46
+ ensure
47
+ set_callback(:save, :after, :enqueue_elastic_search_update)
48
+ end
49
+ end
50
+
51
+ # e.g.
52
+ # Model.will_delete_all(@records) do
53
+ # ...
54
+ # @records.delete_all
55
+ # ...
56
+ # end
57
+ def will_delete_all(ids)
58
+ begin
59
+ skip_callback(:destroy, :after, :enqueue_elastic_search_delete)
60
+ transaction do
61
+ yield if block_given?
62
+ enqueue_elastic_search_delete(ids) unless ids.empty?
63
+ end
64
+ ensure
65
+ set_callback(:destroy, :after, :enqueue_elastic_search_delete)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+ module SearchAndResque
2
+ module ChewyExtensions
3
+ module ChewyType
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def delete!(ids)
8
+ filter(:term => {:_id => ids}).delete_all
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module SearchAndResque
2
+ class Queue
3
+ class << self
4
+ attr_accessor :queue
5
+ end
6
+
7
+ def self.perform(index_name, type_name, action, ids)
8
+ index = Chewy::Index.subclasses.find{ |ind| ind.index_name == index_name }
9
+ type = index.types.find{ |t| t.type_name == type_name }
10
+ type.send(action, ids)
11
+ end
12
+
13
+ def self.enqueue_update(type_name, ids)
14
+ unless ids.empty?
15
+ Resque.enqueue(self, SearchAndResque.index_name, type_name, :import!, ids)
16
+ end
17
+ end
18
+
19
+ def self.enqueue_delete(type_name, ids)
20
+ unless ids.empty?
21
+ Resque.enqueue(self, SearchAndResque.index_name, type_name, :delete!, ids)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module SearchAndResque
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'search_and_resque/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "search_and_resque"
8
+ spec.version = SearchAndResque::VERSION
9
+ spec.authors = ["Peter Woo"]
10
+ spec.email = ["petersnowdonwoo@gmail.com"]
11
+ spec.summary = %q{Resque integration for Chewy}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ end
@@ -0,0 +1,94 @@
1
+ require 'active_record'
2
+
3
+ test_framework = if ActiveRecord::VERSION::STRING >= "4.1"
4
+ require 'minitest/autorun'
5
+ MiniTest::Test
6
+ else
7
+ require 'test/unit'
8
+ Test::Unit::TestCase
9
+ end
10
+
11
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
12
+
13
+ require 'chewy'
14
+ require 'resque'
15
+ require 'search_and_resque'
16
+
17
+ def connect!
18
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', database: ':memory:'
19
+ end
20
+
21
+ def setup!
22
+ connect!
23
+ ActiveRecord::Base.connection.execute 'CREATE TABLE books (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME, title STRING)'
24
+ ActiveRecord::Base.connection.execute 'CREATE TABLE films (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME, title STRING, director STRING)'
25
+ end
26
+
27
+ setup!
28
+
29
+ class Book < ActiveRecord::Base
30
+ search_and_resque :books
31
+ end
32
+
33
+ class Film < ActiveRecord::Base
34
+ search_and_resque :films, :if => ->{ title_changed? }
35
+ end
36
+
37
+ class SearchAndResqueTestIndex < Chewy::Index
38
+ define_type Book, :name => 'books' do
39
+ field :title
40
+ end
41
+
42
+ define_type Film, :name => 'films' do
43
+ field :title
44
+ field :director
45
+ end
46
+ end
47
+
48
+ Chewy.configuration = {
49
+ host: 'localhost:9200'
50
+ }
51
+
52
+ SearchAndResque.index_name = 'search_and_resque_test'
53
+ SearchAndResque::Queue.queue = :search_and_resque_test_queue
54
+
55
+ Resque.inline = true
56
+
57
+
58
+ class SearchAndResqueTest < test_framework
59
+ def setup
60
+ SearchAndResqueTestIndex.purge
61
+ ActiveRecord::Base.connection.tables.each do |table|
62
+ ActiveRecord::Base.connection.execute "DELETE FROM #{table}"
63
+ end
64
+ end
65
+
66
+ def test_index_update_on_create_and_destroy
67
+ book = Book.create
68
+ assert_equal 1, SearchAndResqueTestIndex::Books.filter(:ids => {:values => [book.id]}).total_count
69
+
70
+ book.destroy
71
+ assert_equal 0, SearchAndResqueTestIndex::Books.filter(:ids => {:values => [book.id]}).total_count
72
+ end
73
+
74
+ def test_index_update_on_save
75
+ book = Book.create(:title => 'one')
76
+
77
+ book.update_attributes(:title => 'two')
78
+ assert_equal 'two', SearchAndResqueTestIndex::Books.filter(:ids => {:values => [book.id]}).first.title
79
+ end
80
+
81
+ def test_failing_conditional_index_update
82
+ film = Film.create(:title => 'one', :director => 'two')
83
+
84
+ film.update_attributes(:director => 'three')
85
+ assert_equal 'two', SearchAndResqueTestIndex::Films.filter(:ids => {:values => [film.id]}).first.director
86
+ end
87
+
88
+ def test_succeeding_conditional_index_update
89
+ film = Film.create(:title => 'one', :director => 'two')
90
+
91
+ film.update_attributes(:title => 'three', :director => 'four')
92
+ assert_equal 'four', SearchAndResqueTestIndex::Films.filter(:ids => {:values => [film.id]}).first.director
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: search_and_resque
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Woo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - petersnowdonwoo@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - lib/search_and_resque.rb
54
+ - lib/search_and_resque/callbacks.rb
55
+ - lib/search_and_resque/chewy_extensions.rb
56
+ - lib/search_and_resque/queue.rb
57
+ - lib/search_and_resque/version.rb
58
+ - search_and_resque.gemspec
59
+ - test/search_and_resque_test.rb
60
+ homepage: ''
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Resque integration for Chewy
84
+ test_files:
85
+ - test/search_and_resque_test.rb