sidekiq-activerecord 0.0.1 → 0.0.2

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.
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