sidekiq-activerecord 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
- *.idea
1
+ *.idea
2
+ *.lock
data/.rspec CHANGED
@@ -1 +1,3 @@
1
- --color --format documentation
1
+ -c
2
+ -f d
3
+ -r spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ services:
3
+ - redis-server
4
+ rvm:
5
+ - 1.9.3
6
+ - jruby-19mode
7
+ - rbx
8
+ - 2.0.0
9
+ - 2.1
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: rbx
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in sidekiq-activerecord.gemspec
4
2
  gemspec
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sidekiq-activerecord (0.0.1)
5
- activerecord (~> 4.1.1)
6
- sidekiq (>= 2.16.0)
4
+ sidekiq-activerecord (0.0.2)
5
+ activerecord (>= 4.0)
6
+ sidekiq (>= 2.16)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
@@ -28,7 +28,7 @@ GEM
28
28
  connection_pool (2.0.0)
29
29
  database_cleaner (1.2.0)
30
30
  diff-lcs (1.2.5)
31
- factory_girl (4.2.0)
31
+ factory_girl (4.4.0)
32
32
  activesupport (>= 3.0.0)
33
33
  i18n (0.6.9)
34
34
  json (1.8.1)
@@ -36,15 +36,19 @@ GEM
36
36
  redis (3.0.7)
37
37
  redis-namespace (1.4.1)
38
38
  redis (~> 3.0.4)
39
- rspec (2.14.1)
40
- rspec-core (~> 2.14.0)
41
- rspec-expectations (~> 2.14.0)
42
- rspec-mocks (~> 2.14.0)
43
- rspec-core (2.14.8)
44
- rspec-expectations (2.14.5)
45
- diff-lcs (>= 1.1.3, < 2.0)
46
- rspec-mocks (2.14.6)
47
- sidekiq (3.0.2)
39
+ rspec (3.0.0.rc1)
40
+ rspec-core (= 3.0.0.rc1)
41
+ rspec-expectations (= 3.0.0.rc1)
42
+ rspec-mocks (= 3.0.0.rc1)
43
+ rspec-core (3.0.0.rc1)
44
+ rspec-support (= 3.0.0.rc1)
45
+ rspec-expectations (3.0.0.rc1)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (= 3.0.0.rc1)
48
+ rspec-mocks (3.0.0.rc1)
49
+ rspec-support (= 3.0.0.rc1)
50
+ rspec-support (3.0.0.rc1)
51
+ sidekiq (3.1.0)
48
52
  celluloid (>= 0.15.2)
49
53
  connection_pool (>= 2.0.0)
50
54
  json
@@ -60,8 +64,8 @@ PLATFORMS
60
64
  ruby
61
65
 
62
66
  DEPENDENCIES
63
- database_cleaner (~> 1.2.0)
67
+ database_cleaner (>= 1.2.0)
64
68
  factory_girl (~> 4.0)
65
- rspec (>= 2.14)
69
+ rspec (= 3.0.0.rc1)
66
70
  sidekiq-activerecord!
67
- sqlite3 (~> 1.3.9)
71
+ sqlite3 (>= 1.3.9)
data/README.md CHANGED
@@ -44,7 +44,7 @@ class UserTaskWorker
44
44
  end
45
45
 
46
46
  class UserSyncer
47
- include Sidekiq::ManagerWorker
47
+ include Sidekiq::ActiveRecord::ManagerWorker
48
48
 
49
49
  sidekiq_delegate_task_to :user_task_worker # or UserTaskWorker
50
50
  sidekiq_manager_options :batch_size => 500,
@@ -78,7 +78,7 @@ end
78
78
 
79
79
  ```ruby
80
80
  class UserMailerTaskWorker
81
- include Sidekiq::TaskWorker
81
+ include Sidekiq::ActiveRecord::TaskWorker
82
82
 
83
83
  sidekiq_task_model :user_model # or UserModel
84
84
  sidekiq_task_options :identifier_key => :token
@@ -110,7 +110,7 @@ UserMailerTaskWorker.perform(user.id, :new_email)
110
110
 
111
111
  ## Contributing
112
112
 
113
- 1. Fork it ( http://github.com/<my-github-username>/sidekiq-activerecord/fork )
113
+ 1. Fork it ( http://github.com/yelled3/sidekiq-activerecord/fork )
114
114
  2. Create your feature branch (`git checkout -b my-new-feature`)
115
115
  3. Commit your changes (`git commit -am 'Add some feature'`)
116
116
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -1 +1,12 @@
1
- require "bundler/gem_tasks"
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs'
6
+ task default: :spec
7
+
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ end
11
+
12
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,141 @@
1
+ module Sidekiq
2
+ module ActiveRecord
3
+ module ManagerWorker
4
+ extend Sidekiq::Worker
5
+
6
+ DEFAULT_IDENTIFIER_KEY = :id
7
+ DEFAULT_BATCH_SIZE = 1000
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ base.class_attribute :sidekiq_manager_options_hash
12
+ end
13
+
14
+ module ClassMethods
15
+ # For a given model collection, it delegates each model to a sub-worker (e.g TaskWorker)
16
+ # Specify the TaskWorker with the `sidekiq_delegate_task_to` method.
17
+ #
18
+ # @param models_query ActiveRecord::Relation
19
+ # @param options Hash
20
+ # :worker_class - the worker class to delegate the task to. Alternative to the default `sidekiq_delegate_task_to`
21
+ # :identifier_key - the model identifier column. Default 'id'
22
+ # :additional_keys - additional model keys
23
+ # :batch_size - Specifies the size of the batch. Default to 1000.
24
+ #
25
+ # @example:
26
+ # class UserTaskWorker
27
+ # include Sidekiq::ActiveRecord::TaskWorker
28
+ # end
29
+ #
30
+ # class UserSyncer
31
+ # include Sidekiq::ActiveRecord::ManagerWorker
32
+ #
33
+ # sidekiq_delegate_task_to :user_task_worker # or UserTaskWorker
34
+ # sidekiq_manager_options :batch_size => 500,
35
+ # :identifier_key => :user_token,
36
+ # :additional_keys => [:status]
37
+ # end
38
+ #
39
+ # UserSyncer.perform_query_async(User.active, :batch_size => 300)
40
+ #
41
+ #
42
+ # is equivalent to doing:
43
+ # User.active.each {|user| UserTaskWorker.perform(user.id) }
44
+ #
45
+ def perform_query_async(models_query, options = {})
46
+ set_runtime_options(options)
47
+ models = models_query.select(selected_attributes)
48
+ models.find_in_batches(batch_size: batch_size) do |models_batch|
49
+ model_attributes = models_batch.map { |model| model_attributes(model) }
50
+ Sidekiq::Client.push_bulk(class: worker_class, args: model_attributes)
51
+ end
52
+ end
53
+
54
+ # @required
55
+ # The task worker to delegate to.
56
+ # @param worker_klass (Sidekiq::Worker, Symbol) - UserTaskWorker or :user_task_worker
57
+ def sidekiq_delegate_task_to(worker_klass)
58
+ case worker_klass
59
+ when String, Symbol
60
+ worker_klass.to_s.split('_').map(&:capitalize).join.constantize
61
+ else
62
+ worker_klass
63
+ end
64
+ get_sidekiq_manager_options[:worker_class] = worker_klass
65
+ end
66
+
67
+ # Allows customization for this type of ManagerWorker.
68
+ # Legal options:
69
+ #
70
+ # :worker_class - the worker class to delegate the task to. Alternative to `sidekiq_delegate_task_to`
71
+ # :identifier_key - the model identifier column. Default 'id'
72
+ # :additional_keys - additional model keys
73
+ # :batch_size - Specifies the size of the batch. Default to 1000.
74
+ def sidekiq_manager_options(opts = {})
75
+ self.sidekiq_manager_options_hash = get_sidekiq_manager_options.merge((opts || {}))
76
+ end
77
+
78
+ # private
79
+
80
+ def default_worker_manager_options
81
+ {
82
+ identifier_key: DEFAULT_IDENTIFIER_KEY,
83
+ additional_keys: [],
84
+ batch_size: DEFAULT_BATCH_SIZE
85
+ }
86
+ end
87
+
88
+ # returns the model attributes array:
89
+ # [model_id, attr1, attr2, ...]
90
+ def model_attributes(model)
91
+ additional_attributes = additional_keys.map { |key| model.send(key) }
92
+ id_attribute = model.send(identifier_key)
93
+ additional_attributes.unshift(id_attribute)
94
+ end
95
+
96
+ def selected_attributes
97
+ attrs = [identifier_key, additional_keys]
98
+ attrs << DEFAULT_IDENTIFIER_KEY unless default_identifier? # :id must be included
99
+ attrs
100
+ end
101
+
102
+ def worker_class
103
+ fail NotImplementedError.new('`worker_class` was not specified') unless manager_options[:worker_class].present?
104
+ manager_options[:worker_class]
105
+ end
106
+
107
+ def default_identifier?
108
+ identifier_key == DEFAULT_IDENTIFIER_KEY
109
+ end
110
+
111
+ def identifier_key
112
+ manager_options[:identifier_key]
113
+ end
114
+
115
+ def additional_keys
116
+ manager_options[:additional_keys]
117
+ end
118
+
119
+ def batch_size
120
+ manager_options[:batch_size]
121
+ end
122
+
123
+ def manager_options
124
+ get_sidekiq_manager_options.merge(runtime_options)
125
+ end
126
+
127
+ def get_sidekiq_manager_options
128
+ self.sidekiq_manager_options_hash ||= default_worker_manager_options
129
+ end
130
+
131
+ def runtime_options
132
+ @sidekiq_manager_runtime_options || {}
133
+ end
134
+
135
+ def set_runtime_options(options={})
136
+ @sidekiq_manager_runtime_options = options.delete_if { |_, v| v.to_s.strip == '' }
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,114 @@
1
+ module Sidekiq
2
+ module ActiveRecord
3
+ module TaskWorker
4
+ extend Sidekiq::Worker
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ base.class_attribute :sidekiq_task_options_hash
9
+ end
10
+
11
+ module ClassMethods
12
+ # @example:
13
+ # class UserMailerTaskWorker
14
+ # include Sidekiq::ActiveRecord::TaskWorker
15
+ #
16
+ # sidekiq_task_model :user_model # or UserModel
17
+ # sidekiq_task_options :identifier_key => :token
18
+ #
19
+ # def perform_on_model(user, email_type)
20
+ # UserMailer.deliver_registration_confirmation(user, email_type)
21
+ # end
22
+ #
23
+ # def not_found_model(token)
24
+ # Log.error "User not found for token:#{token}"
25
+ # end
26
+ #
27
+ # def model_valid?(user)
28
+ # user.active?
29
+ # end
30
+ #
31
+ # def invalid_model(user)
32
+ # Log.error "User #{user.token} is invalid"
33
+ # end
34
+ #
35
+ # end
36
+ #
37
+ #
38
+ # UserMailerTaskWorker.perform(user.id, :new_email)
39
+ #
40
+ def perform(identifier, *args)
41
+ model = fetch_model(identifier)
42
+ return not_found_model(identifier) unless model.present?
43
+
44
+ if model_valid?(model)
45
+ perform_on_model(model, *args)
46
+ else
47
+ invalid_model(model)
48
+ end
49
+ end
50
+
51
+ def sidekiq_task_model(model_klass)
52
+ if model_klass.is_a?(String) || model_klass.is_a?(Symbol)
53
+ model_klass = model_klass.to_s.split('_').map(&:capitalize).join.constantize
54
+ else
55
+ model_klass
56
+ end
57
+ get_sidekiq_task_options[:model_class] = model_klass
58
+ end
59
+
60
+ def perform_on_model(model)
61
+ model
62
+ end
63
+
64
+ # recheck the if one of the items is still valid
65
+ def model_valid?(_model)
66
+ true
67
+ end
68
+
69
+ # Hook to handel an invalid model
70
+ def invalid_model(_model)
71
+ end
72
+
73
+ # Hook to handel not found model
74
+ def not_found_model(_identifier)
75
+ end
76
+
77
+ # private
78
+
79
+ def fetch_model(identifier)
80
+ model_class.find_by(identifier_key => identifier)
81
+ end
82
+
83
+ def model_class
84
+ klass = get_sidekiq_task_options[:model_class]
85
+ fail NotImplementedError.new('`model_class` was not specified') unless klass.present?
86
+ klass
87
+ end
88
+
89
+ def identifier_key
90
+ get_sidekiq_task_options[:identifier_key]
91
+ end
92
+
93
+ #
94
+ # Allows customization for this type of TaskWorker.
95
+ # Legal options:
96
+ #
97
+ # :identifier_key - the model identifier column. Default 'id'
98
+ def sidekiq_task_options(opts = {})
99
+ self.sidekiq_task_options_hash = get_sidekiq_task_options.merge((opts || {}).symbolize_keys!)
100
+ end
101
+
102
+ def get_sidekiq_task_options
103
+ self.sidekiq_task_options_hash ||= default_worker_task_options
104
+ end
105
+
106
+ def default_worker_task_options
107
+ {
108
+ identifier_key: :id
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,5 @@
1
+ module Sidekiq
2
+ module ActiveRecord
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -1,10 +1,16 @@
1
-
2
- # gems
1
+ # dependencies
3
2
  require 'sidekiq'
4
3
  require 'active_record'
5
4
 
6
- require "sidekiq/activerecord/version"
5
+ # core
6
+ require 'sidekiq/active_record/version'
7
+
8
+
9
+ module Sidekiq
10
+ module ActiveRecord
11
+ extend ActiveSupport::Autoload
7
12
 
8
- # internal
9
- require 'sidekiq/task_worker'
10
- require 'sidekiq/manager_worker'
13
+ autoload :TaskWorker
14
+ autoload :ManagerWorker
15
+ end
16
+ end
Binary file
Binary file
@@ -1,28 +1,28 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'sidekiq/activerecord/version'
4
+ require 'sidekiq/active_record/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "sidekiq-activerecord"
8
- spec.version = Sidekiq::Activerecord::VERSION
9
- spec.authors = ["Adam Farhi"]
10
- spec.email = ["yelled3@gmail.com"]
7
+ spec.name = 'sidekiq-activerecord'
8
+ spec.version = Sidekiq::ActiveRecord::VERSION
9
+ spec.authors = ['Adam Farhi']
10
+ spec.email = ['afarhi@ebay.com']
11
11
  spec.summary = 'Encapsulates various interactions between Sidekiq and ActiveRecord'
12
- spec.description = 'Encapsulates various interactions between Sidekiq and ActiveRecord'
13
- spec.homepage = "https://github.com/yelled3/sidekiq-activerecord"
14
- spec.license = "MIT"
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/yelled3/sidekiq-activerecord'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
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"]
18
+ spec.test_files = spec.files.grep(%r{^spec/})
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'sidekiq', '>= 2.16.0'
22
- spec.add_dependency 'activerecord', '~> 4.1.1'
21
+ spec.add_dependency 'sidekiq', '>= 2.16'
22
+ spec.add_dependency 'activerecord', '>= 4.0'
23
23
 
24
- spec.add_development_dependency "rspec", ">= 2.14"
25
- spec.add_development_dependency "database_cleaner", '~> 1.2.0'
26
- spec.add_development_dependency 'sqlite3', '~> 1.3.9'
27
- spec.add_development_dependency 'factory_girl', "~> 4.0"
24
+ spec.add_development_dependency 'rspec', '3.0.0.rc1'
25
+ spec.add_development_dependency 'database_cleaner', '>= 1.2.0'
26
+ spec.add_development_dependency 'sqlite3', '>= 1.3.9'
27
+ spec.add_development_dependency 'factory_girl', '~> 4.0'
28
28
  end
@@ -1,6 +1,4 @@
1
- require 'spec_helper'
2
-
3
- describe Sidekiq::ManagerWorker do
1
+ describe Sidekiq::ActiveRecord::ManagerWorker do
4
2
 
5
3
  before do
6
4
  allow(Sidekiq::Client).to receive(:push_bulk)
@@ -12,7 +10,7 @@ describe Sidekiq::ManagerWorker do
12
10
  let(:sidekiq_client) { Sidekiq::Client }
13
11
 
14
12
  class UserManagerWorker
15
- include Sidekiq::ManagerWorker
13
+ include Sidekiq::ActiveRecord::ManagerWorker
16
14
  sidekiq_delegate_task_to MockUserWorker
17
15
  end
18
16
 
@@ -34,7 +32,7 @@ describe Sidekiq::ManagerWorker do
34
32
  end
35
33
 
36
34
  def batch_args(*ids)
37
- {'class' => worker_class, 'args' => ids.map{ |id| [id] }}
35
+ {class: worker_class, args: ids.map{ |id| [id] }}
38
36
  end
39
37
 
40
38
  let(:model_ids) { [[user_1.id], [user_2.id], [user_3.id]] }
@@ -46,7 +44,7 @@ describe Sidekiq::ManagerWorker do
46
44
  let(:custom_worker_class) { MockCustomWorker }
47
45
 
48
46
  def batch_args(*ids)
49
- {'class' => custom_worker_class, 'args' => ids.map{ |id| [id] }}
47
+ {class: custom_worker_class, args: ids.map{ |id| [id] }}
50
48
  end
51
49
 
52
50
  context 'as method arguments' do
@@ -90,7 +88,7 @@ describe Sidekiq::ManagerWorker do
90
88
  around do |example|
91
89
  mock_options(:batch_size => batch_size)
92
90
  example.run
93
- mock_options(:batch_size => Sidekiq::ManagerWorker::DEFAULT_BATCH_SIZE)
91
+ mock_options(:batch_size => Sidekiq::ActiveRecord::ManagerWorker::DEFAULT_BATCH_SIZE)
94
92
  end
95
93
 
96
94
  it 'pushes a bulk of user ids batches' do
@@ -106,7 +104,7 @@ describe Sidekiq::ManagerWorker do
106
104
  let(:additional_keys) { [:email, :status] }
107
105
 
108
106
  def batch_args(*users)
109
- {'class' => worker_class, 'args' => users.map{ |user| [user.id, user.email, user.status] }}
107
+ {class: worker_class, args: users.map{ |user| [user.id, user.email, user.status] }}
110
108
  end
111
109
 
112
110
  context 'as method arguments' do
@@ -134,7 +132,7 @@ describe Sidekiq::ManagerWorker do
134
132
  context 'when the identifier_key is specified' do
135
133
 
136
134
  def batch_args(*users)
137
- {'class' => worker_class, 'args' => users.map{ |user| [user.email] }}
135
+ {class: worker_class, args: users.map{ |user| [user.email] }}
138
136
  end
139
137
 
140
138
  let(:identifier_key) { :email }
@@ -151,7 +149,7 @@ describe Sidekiq::ManagerWorker do
151
149
  around do |example|
152
150
  mock_options(:identifier_key => identifier_key)
153
151
  example.run
154
- mock_options(:identifier_key => Sidekiq::ManagerWorker::DEFAULT_IDENTIFIER_KEY)
152
+ mock_options(:identifier_key => Sidekiq::ActiveRecord::ManagerWorker::DEFAULT_IDENTIFIER_KEY)
155
153
  end
156
154
 
157
155
  it 'pushes a bulk of all user emails as the identifier_key' do
@@ -1,9 +1,7 @@
1
- require 'spec_helper'
2
-
3
- describe Sidekiq::TaskWorker do
1
+ describe Sidekiq::ActiveRecord::TaskWorker do
4
2
 
5
3
  class UserTaskWorker
6
- include Sidekiq::TaskWorker
4
+ include Sidekiq::ActiveRecord::TaskWorker
7
5
  end
8
6
 
9
7
  let!(:user) { create(:user, :active) }
data/spec/spec_helper.rb CHANGED
@@ -1,36 +1,11 @@
1
- require "sidekiq"
2
- require "sidekiq/activerecord"
1
+ require 'sidekiq'
2
+ require 'sidekiq/activerecord'
3
3
  require 'factory_girl'
4
4
  require 'database_cleaner'
5
- require 'support'
6
5
 
7
6
  RSpec.configure do |config|
8
7
  config.alias_example_to :expect_it
9
-
10
- # config.full_backtrace = true
11
-
12
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
13
- # examples within a transaction, remove the following line or assign false
14
- # instead of true.
15
- # config.use_transactional_fixtures = true
16
-
17
- config.expect_with :rspec do |config|
18
- config.syntax = :expect
19
- end
20
-
21
- config.include FactoryGirl::Syntax::Methods # Don't need to write FactoryGirl.create => create
22
-
23
- config.before(:suite) do
24
- DatabaseCleaner.strategy = :transaction
25
- DatabaseCleaner.clean_with(:truncation)
26
- end
27
-
28
- config.before(:each) do
29
- DatabaseCleaner.start
30
- end
31
-
32
- config.after(:each) do
33
- DatabaseCleaner.clean
34
- end
35
8
  end
36
9
 
10
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
11
+
@@ -0,0 +1,10 @@
1
+ db_config = {:adapter => 'sqlite3', :database => ':memory:'}
2
+ ActiveRecord::Base.establish_connection(db_config)
3
+ connection = ActiveRecord::Base.connection
4
+
5
+ connection.create_table :users, force: true do |t|
6
+ t.string :name
7
+ t.string :email
8
+ t.string :status
9
+ t.timestamps
10
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ DatabaseCleaner.strategy = :transaction
4
+ DatabaseCleaner.clean_with(:truncation)
5
+ end
6
+
7
+ config.before(:each) do
8
+ DatabaseCleaner.start
9
+ end
10
+
11
+ config.after(:each) do
12
+ DatabaseCleaner.clean
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryGirl::Syntax::Methods # Don't need to write FactoryGirl.create => create
3
+ end
4
+
5
+ FactoryGirl.define do
6
+ factory :user do
7
+
8
+ sequence(:name) { |n| "name-#{n}" }
9
+ sequence(:email) { |n| "email-#{n}" }
10
+
11
+ trait :active do
12
+ status 'active'
13
+ end
14
+
15
+ trait :banned do
16
+ status 'banned'
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ class User < ActiveRecord::Base
2
+ scope :active, -> { where(:status => :active) }
3
+ scope :banned, -> { where(:status => :banned) }
4
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-20 00:00:00.000000000 Z
12
+ date: 2014-05-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 2.16.0
21
+ version: '2.16'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,45 +26,45 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 2.16.0
29
+ version: '2.16'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: activerecord
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ~>
35
+ - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
- version: 4.1.1
37
+ version: '4.0'
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ~>
43
+ - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
- version: 4.1.1
45
+ version: '4.0'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: rspec
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
- - - ! '>='
51
+ - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: '2.14'
53
+ version: 3.0.0.rc1
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
- - - ! '>='
59
+ - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: '2.14'
61
+ version: 3.0.0.rc1
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: database_cleaner
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
67
- - - ~>
67
+ - - ! '>='
68
68
  - !ruby/object:Gem::Version
69
69
  version: 1.2.0
70
70
  type: :development
@@ -72,7 +72,7 @@ dependencies:
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ~>
75
+ - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: 1.2.0
78
78
  - !ruby/object:Gem::Dependency
@@ -80,7 +80,7 @@ dependencies:
80
80
  requirement: !ruby/object:Gem::Requirement
81
81
  none: false
82
82
  requirements:
83
- - - ~>
83
+ - - ! '>='
84
84
  - !ruby/object:Gem::Version
85
85
  version: 1.3.9
86
86
  type: :development
@@ -88,7 +88,7 @@ dependencies:
88
88
  version_requirements: !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
- - - ~>
91
+ - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: 1.3.9
94
94
  - !ruby/object:Gem::Dependency
@@ -109,27 +109,33 @@ dependencies:
109
109
  version: '4.0'
110
110
  description: Encapsulates various interactions between Sidekiq and ActiveRecord
111
111
  email:
112
- - yelled3@gmail.com
112
+ - afarhi@ebay.com
113
113
  executables: []
114
114
  extensions: []
115
115
  extra_rdoc_files: []
116
116
  files:
117
117
  - .gitignore
118
118
  - .rspec
119
+ - .travis.yml
119
120
  - Gemfile
120
121
  - Gemfile.lock
121
122
  - LICENSE.txt
122
123
  - README.md
123
124
  - Rakefile
125
+ - lib/sidekiq/active_record/manager_worker.rb
126
+ - lib/sidekiq/active_record/task_worker.rb
127
+ - lib/sidekiq/active_record/version.rb
124
128
  - lib/sidekiq/activerecord.rb
125
- - lib/sidekiq/activerecord/version.rb
126
- - lib/sidekiq/manager_worker.rb
127
- - lib/sidekiq/task_worker.rb
129
+ - pkg/sidekiq-activerecord-0.0.1.gem
130
+ - pkg/sidekiq-activerecord-0.0.2.gem
128
131
  - sidekiq-activerecord.gemspec
129
- - spec/lib/sidekiq/manager_worker.rb
130
- - spec/lib/sidekiq/task_worker.rb
132
+ - spec/sidekiq/active_record/manager_worker_spec.rb
133
+ - spec/sidekiq/active_record/task_worker_spec.rb
131
134
  - spec/spec_helper.rb
132
- - spec/support.rb
135
+ - spec/support/database.rb
136
+ - spec/support/database_cleaner.rb
137
+ - spec/support/factory_girl.rb
138
+ - spec/support/models.rb
133
139
  homepage: https://github.com/yelled3/sidekiq-activerecord
134
140
  licenses:
135
141
  - MIT
@@ -156,8 +162,11 @@ signing_key:
156
162
  specification_version: 3
157
163
  summary: Encapsulates various interactions between Sidekiq and ActiveRecord
158
164
  test_files:
159
- - spec/lib/sidekiq/manager_worker.rb
160
- - spec/lib/sidekiq/task_worker.rb
165
+ - spec/sidekiq/active_record/manager_worker_spec.rb
166
+ - spec/sidekiq/active_record/task_worker_spec.rb
161
167
  - spec/spec_helper.rb
162
- - spec/support.rb
168
+ - spec/support/database.rb
169
+ - spec/support/database_cleaner.rb
170
+ - spec/support/factory_girl.rb
171
+ - spec/support/models.rb
163
172
  has_rdoc:
@@ -1,5 +0,0 @@
1
- module Sidekiq
2
- module Activerecord
3
- VERSION = "0.0.1"
4
- end
5
- end
@@ -1,144 +0,0 @@
1
- module Sidekiq
2
- module ManagerWorker
3
- extend Sidekiq::Worker
4
-
5
- DEFAULT_IDENTIFIER_KEY = :id
6
- DEFAULT_BATCH_SIZE = 1000
7
-
8
- def self.included(base)
9
- base.extend(ClassMethods)
10
- base.class_attribute :sidekiq_manager_options_hash
11
- end
12
-
13
- module ClassMethods
14
-
15
- # For a given model collection, it delegates each model to a sub-worker (e.g TaskWorker)
16
- # Specify the TaskWoker with the `sidekiq_delegate_task_to` method.
17
- #
18
- # @param models_query ActiveRecord::Relation
19
- # @param options Hash
20
- # :worker_class - the worker class to delegate the task to. Alternative to the default `sidekiq_delegate_task_to`
21
- # :identifier_key - the model identifier column. Default 'id'
22
- # :additional_keys - additional model keys
23
- # :batch_size - Specifies the size of the batch. Default to 1000.
24
- #
25
- # @example:
26
- # class UserTaskWorker
27
- # include Sidekiq::TaskWorker
28
- # end
29
- #
30
- # class UserSyncer
31
- # include Sidekiq::ManagerWorker
32
- #
33
- # sidekiq_delegate_task_to :user_task_worker # or UserTaskWorker
34
- # sidekiq_manager_options :batch_size => 500,
35
- # :identifier_key => :user_token,
36
- # :additional_keys => [:status]
37
- # end
38
- #
39
- # UserSyncer.perform_query_async(User.active, :batch_size => 300)
40
- #
41
- #
42
- # is equivalent to doing:
43
- # User.active.each {|user| UserTaskWorker.peform(user.id) }
44
- #
45
- def perform_query_async(models_query, options={})
46
- set_runtime_options(options)
47
- models = models_query.select(selected_attributes)
48
- models.find_in_batches(batch_size: batch_size) do |models_batch|
49
- model_attributes = models_batch.map { |model| model_attributes(model) }
50
- Sidekiq::Client.push_bulk('class' => worker_class, 'args' => model_attributes)
51
- end
52
- # set_runtime_options(nil)
53
- end
54
-
55
- # @required
56
- # The task worker to delegate to.
57
- # @param worker_klass (Sidekiq::Worker, Symbol) - UserTaskWorker or :user_task_worker
58
- def sidekiq_delegate_task_to(worker_klass)
59
- if worker_klass.is_a?(String) or is_a?(Symbol)
60
- worker_klass.to_s.split('_').collect(&:capitalize).join.constantize
61
- else
62
- worker_klass
63
- end
64
- self.get_sidekiq_manager_options[:worker_class] = worker_klass
65
- end
66
-
67
- # Allows customization for this type of ManagerWorker.
68
- # Legal options:
69
- #
70
- # :worker_class - the worker class to delegate the task to. Alternative to `sidekiq_delegate_task_to`
71
- # :identifier_key - the model identifier column. Default 'id'
72
- # :additional_keys - additional model keys
73
- # :batch_size - Specifies the size of the batch. Default to 1000.
74
- def sidekiq_manager_options(opts={})
75
- self.sidekiq_manager_options_hash = get_sidekiq_manager_options.merge((opts || {}).symbolize_keys!)
76
- end
77
-
78
-
79
- # private
80
-
81
- def default_worker_manager_options
82
- {
83
- :identifier_key => DEFAULT_IDENTIFIER_KEY,
84
- :additional_keys => [],
85
- :worker_class => nil,
86
- :batch_size => DEFAULT_BATCH_SIZE,
87
- }
88
- end
89
-
90
- # returns the model attributes array:
91
- # [model_id, attr1, attr2, ...]
92
- def model_attributes(model)
93
- additional_attributes = additional_keys.map { |key| model.send(key) }
94
- id_attribute = model.send(identifier_key)
95
- additional_attributes.unshift(id_attribute)
96
- end
97
-
98
- def selected_attributes
99
- attrs = [identifier_key, additional_keys]
100
- attrs << DEFAULT_IDENTIFIER_KEY unless default_identifier? # :id must be included
101
- attrs
102
- end
103
-
104
- def worker_class
105
- raise NotImplementedError.new('`worker_class` was not specified') unless manager_options[:worker_class].present?
106
- manager_options[:worker_class]
107
- end
108
-
109
- def default_identifier?
110
- identifier_key == DEFAULT_IDENTIFIER_KEY
111
- end
112
-
113
- def identifier_key
114
- manager_options[:identifier_key]
115
- end
116
-
117
- def additional_keys
118
- manager_options[:additional_keys]
119
- end
120
-
121
- def batch_size
122
- manager_options[:batch_size]
123
- end
124
-
125
- def manager_options
126
- self.get_sidekiq_manager_options.merge(runtime_options)
127
- end
128
-
129
- def get_sidekiq_manager_options
130
- self.sidekiq_manager_options_hash ||= default_worker_manager_options
131
- end
132
-
133
- def runtime_options
134
- @sidekiq_manager_runtime_options || {}
135
- end
136
-
137
- def set_runtime_options(options)
138
- options = options.delete_if { |k, v| v.nil? } if options.present?
139
- @sidekiq_manager_runtime_options = options
140
- end
141
-
142
- end
143
- end
144
- end
@@ -1,117 +0,0 @@
1
- module Sidekiq
2
- module TaskWorker
3
- extend Sidekiq::Worker
4
-
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- base.class_attribute :sidekiq_task_options_hash
8
- end
9
-
10
- module ClassMethods
11
-
12
- # @example:
13
- # class UserMailerTaskWorker
14
- # include Sidekiq::TaskWorker
15
- #
16
- # sidekiq_task_model :user_model # or UserModel
17
- # sidekiq_task_options :identifier_key => :token
18
- #
19
- # def perform_on_model(user, email_type)
20
- # UserMailer.deliver_registration_confirmation(user, email_type)
21
- # end
22
- #
23
- # def not_found_model(token)
24
- # Log.error "User not found for token:#{token}"
25
- # end
26
- #
27
- # def model_valid?(user)
28
- # user.active?
29
- # end
30
- #
31
- # def invalid_model(user)
32
- # Log.error "User #{user.token} is invalid"
33
- # end
34
- #
35
- # end
36
- #
37
- #
38
- # UserMailerTaskWorker.perform(user.id, :new_email)
39
- #
40
- def perform(identifier, *args)
41
- model = fetch_model(identifier)
42
- return not_found_model(identifier) unless model.present?
43
-
44
- if model_valid?(model)
45
- perform_on_model(model, *args)
46
- else
47
- invalid_model(model)
48
- end
49
- end
50
-
51
- def sidekiq_task_model(model_klass)
52
- if model_klass.is_a?(String) or model_klass.is_a?(Symbol)
53
- model_klass = model_klass.to_s.split('_').collect(&:capitalize).join.constantize
54
- else
55
- model_klass
56
- end
57
- self.get_sidekiq_task_options[:model_class] = model_klass
58
- end
59
-
60
- def perform_on_model(model)
61
- model
62
- end
63
-
64
- # recheck the if one of the items is still valid
65
- def model_valid?(model)
66
- true
67
- end
68
-
69
- # Hook to handel an invalid model
70
- def invalid_model(model)
71
- end
72
-
73
- # Hook to handel not found model
74
- def not_found_model(identifier)
75
- end
76
-
77
-
78
- # private
79
-
80
- def fetch_model(identifier)
81
- model_class.find_by(identifier_key => identifier)
82
- end
83
-
84
- def model_class
85
- klass = self.get_sidekiq_task_options[:model_class]
86
- raise NotImplementedError.new('`model_class` was not specified') unless klass.present?
87
- klass
88
- end
89
-
90
- def identifier_key
91
- self.get_sidekiq_task_options[:identifier_key]
92
- end
93
-
94
- #
95
- # Allows customization for this type of TaskWorker.
96
- # Legal options:
97
- #
98
- # :identifier_key - the model identifier column. Default 'id'
99
- def sidekiq_task_options(opts={})
100
- self.sidekiq_task_options_hash = get_sidekiq_task_options.merge((opts || {}).symbolize_keys!)
101
- end
102
-
103
- def get_sidekiq_task_options
104
- self.sidekiq_task_options_hash ||= default_worker_task_options
105
- end
106
-
107
- def default_worker_task_options
108
- {
109
- :identifier_key => :id,
110
- :model_class => nil
111
- }
112
- end
113
-
114
- end
115
-
116
- end
117
- end
data/spec/support.rb DELETED
@@ -1,39 +0,0 @@
1
- require "active_record"
2
-
3
- def set_database
4
- db_config = {:adapter => "sqlite3", :database => ":memory:"}
5
- ActiveRecord::Base.establish_connection(db_config)
6
- connection = ActiveRecord::Base.connection
7
-
8
- connection.create_table :users, force: true do |t|
9
- t.string :name
10
- t.string :email
11
- t.string :status
12
- t.timestamps
13
- end
14
- end
15
-
16
- set_database
17
-
18
- class User < ActiveRecord::Base
19
- scope :active, -> { where(:status => :active) }
20
- scope :banned, -> { where(:status => :banned) }
21
- end
22
-
23
-
24
- FactoryGirl.define do
25
- factory :user do
26
-
27
- sequence(:name) { |n| "name-#{n}" }
28
- sequence(:email) { |n| "email-#{n}" }
29
-
30
- trait :active do
31
- status 'active'
32
- end
33
-
34
- trait :banned do
35
- status 'banned'
36
- end
37
-
38
- end
39
- end