README.markdown in tennis-jobs-0.3.1 vs README.markdown in tennis-jobs-0.4.0

- old
+ new

@@ -1,207 +1,76 @@ -This small library is intended to help creating asynchronous jobs -using Ruby and RabbitMQ via the Sneakers gem. +This is a celluloid based background jobs library heavily inspired by [Sidekiq][sidekiq]. +The difference is that it allows you to change the backend, Redis, to other ones like RabitMQ. +You can start with something _à la_ [sucker_punch][sucker_punch]: background jobs threaded within the application process +and switch to separated processes later using another backend. <a target="_blank" href="https://travis-ci.org/nicoolas25/tennis"><img src="https://travis-ci.org/nicoolas25/tennis.svg?branch=master" /></a> <a target="_blank" href="https://codeclimate.com/github/nicoolas25/tennis"><img src="https://codeclimate.com/github/nicoolas25/tennis/badges/gpa.svg" /></a> <a target="_blank" href="https://codeclimate.com/github/nicoolas25/tennis/coverage"><img src="https://codeclimate.com/github/nicoolas25/tennis/badges/coverage.svg" /></a> <a target="_blank" href="https://rubygems.org/gems/tennis-jobs"><img src="https://badge.fury.io/rb/tennis-jobs.svg" /></a> -## Features - -- Hooks: `.before(symbol, &block)` -- Serializers: `.serialize(loader:)` -- Helpers for defering method calls: `object.defer.method(*arguments)` - -**Extra** - -- A `Tennis::Serializer::Generic` handling classes and ActiveRecord objects - ## Configuration -The background job require a group of processes to handle the tasks you want to -do asynchronously. Tennis uses YAML configuration file in order to launch thoses -processes. +Install the gem in your Gemfile: -``` yaml -# tennis.conf.yml -group1: - exchange: "default" - workers: 1 - classes: - - "Scope::MyClass" - - "Scope::Model" -group2: - exchange: "important" - workers: 10 - classes: - - "Only::ImportantWorker" -``` - -Here we see two groups of worker. Each group can be launch with the `tennis` -command: - - $ bundle exec tennis group1 - -The `workers` options is directly given to sneakers, it will determine the -number of subprocesses that will handle the messages, the level of parallelism. - -The classes are the classes that will receive your `send_work` or `defer` calls -but we'll see that later... - -Also it is possible to add options directly in your workers: - ``` ruby -module WorkerHelpers - BeforeFork = -> { ActiveRecord::Base.connection_pool.disconnect! rescue nil } - AfterFork = -> { ActiveRecord::Base.establish_connection } -end - -class MyClass - include Tennis::Worker::Generic - - set_option :before_fork, WorkerHelpers::BeforeFork - set_option :after_fork, WorkerHelpers::AfterFork - set_option :handler, Sneakers::Handlers::MaxretryWithoutErrors - - work do |message| - MyActiveRecordModel.create(message: message) - end -end +gem "tennis-jobs" +gem "tennis-jobs-redis" # Not available at the moment ``` -In this example I use constants to store the Proc options. This is because -having different options for the workers of a same group isn't possible. By -using this constant, I can have another worker with those same options and put -it in the same group. +Configure Tennis in your `config/application.rb` (or any other file): -## Examples - -Those examples are what we wish to achieve. - -The name of the queue is the name of the class by default and can be reset -using `set_option :queue_name, "your-queue-name"`. - -### Hooks - ``` ruby -class MyClass - include Tennis::Worker::Generic +Tennis.configure do |config| + config.backend Tennis::Backend::Redis.new(redis_url) - before do - puts "Before processing" - end - - work do |message| - puts "Working with #{message}" - ack! - end + # require "logger" + # config.logger = Logger.new(STDOUT) end - -MyClass.send_work("my class") -# => Before processing -# => Working with my class ``` -### Serializers +Start tennis from the command line: -You can provide a Proc for the loader and/or the dumper keywords. -The dumper will be used when calling `MyClass.send_work(message)` receiving -the `message` as argument. It should return a string. The loader will be -used when the message is poped from the RabbitMQ queue. - -``` ruby -class MyClass - include Tennis::Worker::Generic - - serialize loader: ->(message){ JSON.parse(message) }, - dumper: ->(message){ JSON.generate(message) } - - work do |message| - one, two = message - puts "Message is serialized and deserialized correctly" - puts "one: #{one}, two: #{two}" - ack! - end -end - -MyClass.send_work([1, "foo"]) -# => Message is serialized and deserialized correctly -# => one: 1, two: foo ``` +bundle exec tennis --concurrency 4 --require ./config/application.rb --jobs "MyJob,MyOtherJob" -``` ruby -class MyClass - include Tennis::Worker::Generic - - serialize Tennis::Serializer::Generic.new - - work do |message| - klass, active_record_object = message - puts "Classes can be passed: #{klass.name} - #{klass.class}" - puts "Active record object can be passed too: #{active_record_object}" - ack! - end -end - -MyClass.send_work([String, User.find(1)]) -# => Classes can be passed: String - Class -# => Active record object can be passed too: <User#1 ...> +# There is also a shorter equivalent: +# bundle exex tennis -c 4 -r ./config/application.rb -j "MyJob,MyOtherJob" ``` -### Helpers +## Usage -Any class method can be defered: - ``` ruby -class MyClass - include Tennis::Worker::Deferable +MINUTES = 60 - def self.my_method(user) - puts "Running my method on #{user}" +class MyJob + include Tennis::Job + + def my_method(*args) + puts "=> #{args}.sum = args.inject(0, &:+)" end end -MyClass.defer.my_method(User.find(1)) -# => Running my method on <User#1 ...> -``` +my_job_instance = MyJob.new +my_job_instance.async.my_method(1, 2, 3) -An ActiveRecord::Base instance can be the receiver if it has an `id`: +# Will print in your `tennis` process: +# => [1, 2, 3].sum = 6 -``` ruby -class MyModel < ActiveRecord::Base - include Tennis::Worker::Deferable +my_job_instance.async_in(2 * MINUTES).my_method(4, 5, 6) - def my_method - puts "Running my method on #{self}" - end -end - -instance = MyModel.find(1) -instance.defer.my_method -# => Running my method on <MyModel#1 ...> +# Will print, in approximatively two minutes, in your `tennis` process: +# => [4, 5, 6].sum = 15 ``` -Handling errors and results for the deferred methods is via `on_error` and -`on_success` keyword. You must return `ack!` or `reject!` here as in a -Sneakers' `work` method. +The `my_method`'s arguments can be quite complex depending on your backend support. +The same goes for the `MyJob`'s instance. -``` ruby -class MyModel < ActiveRecord::Base - include Tennis::Worker::Deferable +With the `Tennis::Backend::Memory` backend, you can use anything and it will be kept as it is. - on_error do |exception| - puts "I just saved the day handling a #{exception}" - reject! - end +## Testing - def my_method - puts "Running my method on #{self}" - raise StandardError - end -end +This section is waiting to beeing written. -instance = MyModel.find(1) -instance.defer.my_method -# => Running my method on <MyModel#1 ...> -# => I just saved the day handling a StandardError -``` + +[sidekiq]: https://github.com/mperham/sidekiq +[sucker_punch]: https://github.com/brandonhilkert/sucker_punch