search_and_resque 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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