localjob 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: becc6c53c0de0692e654c0e4fd8b220b13baa424
4
- data.tar.gz: 770a92796d0fedf97c3d66728e039b3d966e7f92
3
+ metadata.gz: 6c33dc84596d653899831a3697428d950028e0c5
4
+ data.tar.gz: 8547e6ca4022b618956578e8dc3f11fe65ae8b00
5
5
  SHA512:
6
- metadata.gz: f56f0b1ae32cda8650cd0e6375278ad34bf16c00db257a180d1070c980c7eee422f7e604afcbbe013c7da7c0ae403d2f0a7c795987fd52330f101fd19f3a985e
7
- data.tar.gz: 3ecf92a571bd824625ea348b5f0ccdf8e4f5b7e3ba2fc3ab468a1915c90a768f5a250803d71a8a1caf41387c21d237320f605c250e6edcf4bdbcc035d4b1c90f
6
+ metadata.gz: 22eab445b06c1011c53a244a2677feec403a4f501e0dc35ca663b405d4d754f3f891914420fb811a5a840076f1fbd6af71f7afc31d5f1e9c76a297730fbf281c
7
+ data.tar.gz: d7ede95238c734c9ab043b2861152203197fb7606a86ee6b81083ce304c9f479cbaf50842515301518f6566733b7978a1b77dff828827df476e9dd5505191199
data/README.md CHANGED
@@ -12,18 +12,18 @@ project is to be able to migrate to another background queue system by switching
12
12
  adapter: `Localjob.adapter = Resque` to switch to Resque, without changes to
13
13
  your own code.
14
14
 
15
- The POSIX message queue is persistent till reboot. You will need to tune system
16
- parameters for your application, please consult [posix-mqueue][pmq-gem]'s
15
+ The POSIX message queue is persistent till reboot. You **will need to tune system
16
+ parameters for your application**, please consult [posix-mqueue][pmq-gem]'s
17
17
  documentation.
18
18
 
19
- Localjob works on Ruby >= 2.0.0 and Linux.
19
+ Localjob works on Ruby >= 2.0.0. On Linux, it will use the POSIX message queue.
20
+ On OS X (and Windows, not tested) it will use a mock class instead of the
21
+ message queue, to aid you in testing and running Localjob in development.
20
22
 
21
- WIP not everything works as advertised, but try it out and report anything that
22
- doesn't work! It's not released as a gem yet, so add the following to your
23
- Gemfile to use it:
23
+ Add it to your Gemfile:
24
24
 
25
25
  ```ruby
26
- gem 'localjob', git: "git@github.com:Sirupsen/localjob.git"
26
+ gem 'localjob'
27
27
  ```
28
28
 
29
29
  ## Usage
@@ -49,8 +49,88 @@ queue = Localjob.new
49
49
  queue << EmailJob.new(current_user.id, welcome_email)
50
50
  ```
51
51
 
52
- Then spawn a worker with `localjob work`. It takes a few arguments, like `-d` to
53
- deamonize itself. `localjob help work` to list all options.
52
+ A job is serialized with YAML and pushed onto a persistent POSIX message queue.
53
+ This means a worker does not have to listen on the queue to push things to it.
54
+ Workers will pop off the message queue, but only one will receive the job.
55
+ Deserialize the message to create an instance of your object, and call
56
+ `#perform` on the object.
57
+
58
+ ### Rails initializer
59
+
60
+ For easy access to your queues in Rails, you can add an initializer to set up a
61
+ constant referencing each of your queues. This allows easy access anywhere in
62
+ your app. In `config/initializers/localjob.rb`:
63
+
64
+ ```ruby
65
+ BackgroundQueue = Localjob.new("main-queue")
66
+ ```
67
+
68
+ Then in your app you can simply reference the constant to push to the queue:
69
+
70
+ ```ruby
71
+ BackgroundQueue << EmailJob.new(current_user.id, welcome_email)
72
+ ```
73
+
74
+ ### Managing workers
75
+
76
+ Spawning workers can be done with `localjob`. Run `localjob work` to spawn a
77
+ single worker. It takes a few arguments. The most important being `--require`
78
+ which takes a path the worker will require before processing jobs. For Rails,
79
+ you can run `localjob work` without any arguments. `localjob(2)` has a few other
80
+ commands such as `list` to list all queues and `size` to list the size of all
81
+ queues. `localjob help` to list all commands.
82
+
83
+ Gracefully shut down workers by sending `SIGQUIT` to them. This will make sure
84
+ the worker completes its current job before shutting down. Jobs can be sent to
85
+ the queue meanwhile, and the worker will process them once it starts again.
86
+
87
+ ### Queues
88
+
89
+ Localjobs supports multiple queues, and workers can be assigned to queues. By
90
+ default everything is on a single queue. To push to a named queue:
91
+
92
+ ```ruby
93
+ email = Localjob.new("email")
94
+ email << EmailJob.new(current_user.id, welcome_email)
95
+ ```
96
+
97
+ The worker spawn command `localjob work` takes a `--queues` argument which is a
98
+ comma seperated list of queues to listen on, e.g. `localjob work --queues email,webhooks`.
99
+
100
+ ### Testing
101
+
102
+ Create your instance of the queue as normal in your setup:
103
+
104
+ ```ruby
105
+ def setup
106
+ @queue = LocalJob.new("test-queue")
107
+ end
108
+ ```
109
+
110
+ In your `teardown` you'll want to destroy your queue:
111
+
112
+ ```ruby
113
+ def teardown
114
+ @queue.destroy
115
+ end
116
+ ```
117
+
118
+ You can get the size of your queue by calling `@queue.size`. You pop off the
119
+ queue with `@queue.shift`. Other than that, just use the normal API. You can
120
+ also read the tests for Localjob to get an idea of how to test. Sample test:
121
+
122
+ ```ruby
123
+ def test_pop_and_send_to_worker
124
+ WalrusJob.any_instance.expects(:perform)
125
+
126
+ @localjob << WalrusJob.new("move")
127
+
128
+ job = @localjob.shift
129
+ @worker.process(job)
130
+
131
+ assert_equal 0, @localjob.size
132
+ end
133
+ ```
54
134
 
55
135
  [pmq]: http://linux.die.net/man/7/mq_overview
56
136
  [pmq-gem]: https://github.com/Sirupsen/posix-mqueue
data/ext/mkrf_conf.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'rubygems/command.rb'
3
+ require 'rubygems/dependency_installer.rb'
4
+ begin
5
+ Gem::Command.build_args = ARGV
6
+ rescue NoMethodError
7
+ end
8
+
9
+ inst = Gem::DependencyInstaller.new
10
+
11
+ begin
12
+ if RUBY_PLATFORM =~ /linux/
13
+ inst.install "posix-mqueue", "0.0.7"
14
+ end
15
+ rescue
16
+ exit(1)
17
+ end
@@ -11,10 +11,12 @@ class Localjob
11
11
  end
12
12
 
13
13
  def shift
14
- (queue,), = IO.select(@queues)
15
- queue.shift
16
- rescue POSIX::Mqueue::QueueEmpty
17
- retry
14
+ begin
15
+ (queue,), = IO.select(@queues)
16
+ queue.shift
17
+ rescue POSIX::Mqueue::QueueEmpty
18
+ retry
19
+ end
18
20
  end
19
21
 
20
22
  private
@@ -0,0 +1,34 @@
1
+ class Localjob
2
+ class LinuxAdapter
3
+ attr_reader :mqueue
4
+
5
+ def initialize(name)
6
+ @mqueue = POSIX::Mqueue.new(fix_queue_name(name))
7
+ end
8
+
9
+ def receive
10
+ @mqueue.timedreceive
11
+ end
12
+
13
+ def send(message)
14
+ @mqueue.timedsend message
15
+ end
16
+
17
+ def size
18
+ @mqueue.size
19
+ end
20
+
21
+ def destroy
22
+ @mqueue.unlink
23
+ end
24
+
25
+ def to_io
26
+ @mqueue.to_io
27
+ end
28
+
29
+ private
30
+ def fix_queue_name(name)
31
+ name.start_with?('/') ? name : "/#{name}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ class Localjob
2
+ class Channel
3
+ def shift
4
+ queue = @queues.find { |q| q.size > 0 }
5
+ return queue.shift
6
+ end
7
+ end
8
+
9
+ class MockAdapter
10
+ def initialize(name = 'default')
11
+ @@queues ||= {}
12
+ @name = name
13
+ @@queues[@name] ||= []
14
+ end
15
+
16
+ def receive
17
+ @@queues[@name].shift
18
+ end
19
+
20
+ def send(message)
21
+ @@queues[@name] << message
22
+ end
23
+
24
+ def size
25
+ @@queues[@name].size
26
+ end
27
+
28
+ def destroy
29
+ @@queues[@name] = nil
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  class Localjob
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/localjob.rb CHANGED
@@ -1,16 +1,26 @@
1
- require 'posix/mqueue'
1
+ begin
2
+ require 'posix/mqueue'
3
+ rescue LoadError
4
+ end
5
+
2
6
  require 'yaml'
3
7
  require 'logger'
8
+ require 'forwardable'
4
9
 
5
10
  require "localjob/version"
6
11
  require 'localjob/channel'
7
12
  require 'localjob/worker'
8
13
 
9
14
  class Localjob
10
- attr_reader :queue_name
15
+ extend Forwardable
16
+
17
+ attr_reader :name
18
+ attr_accessor :queue
19
+
20
+ def_delegators :queue, :to_io, :destroy, :size
11
21
 
12
- def initialize(queue = "localjob")
13
- @queue_name = fix_queue_name(queue)
22
+ def initialize(name = "localjob")
23
+ @name = name
14
24
  end
15
25
 
16
26
  def serializer
@@ -18,31 +28,23 @@ class Localjob
18
28
  end
19
29
 
20
30
  def queue
21
- @queue ||= POSIX::Mqueue.new(@queue_name)
22
- end
31
+ return @queue if @queue
23
32
 
24
- def <<(object)
25
- queue.timedsend serializer.dump(object)
33
+ case RUBY_PLATFORM
34
+ when /linux/
35
+ require 'localjob/linux_adapter'
36
+ @queue = LinuxAdapter.new(@name)
37
+ else
38
+ require 'localjob/mock_adapter'
39
+ @queue = MockAdapter.new(@name)
40
+ end
26
41
  end
27
42
 
28
- def size
29
- queue.size
43
+ def <<(object)
44
+ queue.send serializer.dump(object)
30
45
  end
31
46
 
32
47
  def shift
33
- serializer.load queue.timedreceive
34
- end
35
-
36
- def destroy
37
- queue.unlink
38
- end
39
-
40
- def to_io
41
- queue.to_io
42
- end
43
-
44
- private
45
- def fix_queue_name(queue)
46
- queue.start_with?('/') ? queue : "/#{queue}"
48
+ serializer.load queue.receive
47
49
  end
48
50
  end
data/localjob.gemspec CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
+ spec.extensions = ["ext/mkrf_conf.rb"]
20
21
 
21
- spec.add_dependency "posix-mqueue", "0.0.9"
22
22
  spec.add_dependency "thor", "0.18.1"
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
@@ -18,12 +18,6 @@ class LocaljobTest < LocaljobTestCase
18
18
  assert_equal "move", job.action
19
19
  end
20
20
 
21
- def test_throws_error_if_message_is_too_large
22
- assert_raises Errno::EMSGSIZE do
23
- @localjob << AngryWalrusJob.new("f" * @localjob.queue.msgsize)
24
- end
25
- end
26
-
27
21
  def test_handles_multiple_queues
28
22
  @localjob << WalrusJob.new("move")
29
23
 
@@ -33,4 +27,12 @@ class LocaljobTest < LocaljobTestCase
33
27
  assert_equal 1, @localjob.size
34
28
  assert_equal 1, other.size
35
29
  end
30
+
31
+ on_platform 'linux' do
32
+ def test_throws_error_if_message_is_too_large
33
+ assert_raises Errno::EMSGSIZE do
34
+ @localjob << AngryWalrusJob.new("f" * @localjob.queue.mqueue.msgsize)
35
+ end
36
+ end
37
+ end
36
38
  end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class MockAdapterTest < LocaljobTestCase
4
+ def setup
5
+ @localjob = queue
6
+ @localjob.queue = Localjob::MockAdapter.new("localjob")
7
+ end
8
+
9
+ def test_push_to_queue
10
+ @localjob << "hello world"
11
+ assert_equal 1, @localjob.size
12
+ end
13
+
14
+ def test_push_and_pop_from_queue
15
+ @localjob << "hello world"
16
+ assert_equal "hello world", @localjob.shift
17
+ end
18
+
19
+ def test_destroy_queue
20
+ @localjob << "hello world"
21
+ end
22
+ end
data/test/test_helper.rb CHANGED
@@ -41,4 +41,10 @@ class LocaljobTestCase < MiniTest::Unit::TestCase
41
41
  # It's slower. But safe.
42
42
  GC.start
43
43
  end
44
+
45
+ def self.on_platform(platform)
46
+ if RUBY_PLATFORM =~ /#{platform}/
47
+ yield
48
+ end
49
+ end
44
50
  end
data/test/worker_test.rb CHANGED
@@ -18,38 +18,26 @@ class WorkerTest < LocaljobTestCase
18
18
  def test_working_off_queue_in_child
19
19
  @localjob << WalrusJob.new("move")
20
20
 
21
- fork do
21
+ a = Thread.start {
22
22
  job = @localjob.shift
23
23
  @worker.process(job)
24
- end
25
-
26
- Process.wait
27
- assert_equal 0, @localjob.size
28
- end
29
-
30
- def test_sigquit_terminates_the_worker
31
- @localjob << WalrusJob.new("move")
32
-
33
- assert_equal 1, @localjob.size
34
-
35
- pid = fork { @worker.work }
24
+ }
36
25
 
37
- Process.kill("QUIT", pid)
38
- Process.wait
26
+ a.join
39
27
 
40
28
  assert_equal 0, @localjob.size
41
29
  end
42
30
 
31
+
43
32
  def test_doesnt_stop_on_error
44
33
  @localjob << AngryWalrusJob.new(100)
45
34
  @localjob << WalrusJob.new("be happy")
46
35
 
47
- pid = fork { @worker.work }
36
+ a = Thread.start { @worker.work }
48
37
 
49
38
  # Hack to account for race condition, 0.01s should be plenty
50
39
  sleep 0.01
51
- Process.kill("QUIT", pid)
52
- Process.wait
40
+ a.kill
53
41
 
54
42
  assert_equal 0, @localjob.size
55
43
  end
@@ -62,24 +50,36 @@ class WorkerTest < LocaljobTestCase
62
50
 
63
51
  @worker.channel << 'other-queue'
64
52
 
65
- pid = fork { @worker.work }
53
+ a = Thread.start { @worker.work }
66
54
 
67
- # Hack to account for race condition, 0.01s should be plenty
68
55
  sleep 0.01
69
- Process.kill("QUIT", pid)
70
- Process.wait
56
+ a.kill
71
57
 
72
58
  assert_equal 0, @localjob.size
73
59
  assert_equal 0, other.size
74
60
  end
75
61
 
76
62
  def test_worker_doesnt_die_on_bad_serialization
77
- @localjob.queue.timedsend "--- !ruby/object:Whatever {}\n"
63
+ @localjob.queue.send "--- !ruby/object:Whatever {}\n"
78
64
 
79
- pid = fork { @worker.work }
65
+ a = Thread.start { @worker.work }
80
66
 
81
67
  sleep 0.01
82
- Process.kill("QUIT", pid)
83
- Process.wait
68
+ a.kill
69
+ end
70
+
71
+ on_platform 'linux' do
72
+ def test_sigquit_terminates_the_worker
73
+ @localjob << WalrusJob.new("move")
74
+
75
+ assert_equal 1, @localjob.size
76
+
77
+ pid = fork { @worker.work }
78
+
79
+ Process.kill("QUIT", pid)
80
+ Process.wait
81
+
82
+ assert_equal 0, @localjob.size
83
+ end
84
84
  end
85
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localjob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Eskildsen
@@ -10,20 +10,6 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2013-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: posix-mqueue
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - '='
18
- - !ruby/object:Gem::Version
19
- version: 0.0.9
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - '='
25
- - !ruby/object:Gem::Version
26
- version: 0.0.9
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: thor
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -86,7 +72,8 @@ email:
86
72
  - sirup@sirupsen.com
87
73
  executables:
88
74
  - localjob
89
- extensions: []
75
+ extensions:
76
+ - ext/mkrf_conf.rb
90
77
  extra_rdoc_files: []
91
78
  files:
92
79
  - .gitignore
@@ -95,14 +82,18 @@ files:
95
82
  - README.md
96
83
  - Rakefile
97
84
  - bin/localjob
85
+ - ext/mkrf_conf.rb
98
86
  - lib/localjob.rb
99
87
  - lib/localjob/channel.rb
100
88
  - lib/localjob/cli.rb
89
+ - lib/localjob/linux_adapter.rb
90
+ - lib/localjob/mock_adapter.rb
101
91
  - lib/localjob/version.rb
102
92
  - lib/localjob/worker.rb
103
93
  - localjob.gemspec
104
94
  - test/jobs.rb
105
95
  - test/localjob_test.rb
96
+ - test/mock_adapter.rb
106
97
  - test/test_helper.rb
107
98
  - test/worker_test.rb
108
99
  homepage: ''
@@ -133,5 +124,6 @@ summary: Simple, self-contained background queue built on top of POSIX message q
133
124
  test_files:
134
125
  - test/jobs.rb
135
126
  - test/localjob_test.rb
127
+ - test/mock_adapter.rb
136
128
  - test/test_helper.rb
137
129
  - test/worker_test.rb