afterparty 0.0.2 → 0.0.3
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 +4 -4
- data/.travis.yml +3 -1
- data/README.md +4 -2
- data/afterparty.gemspec +1 -0
- data/app/assets/javascripts/afterparty.js.coffee +14 -0
- data/app/assets/stylesheets/afterparty.css.sass +85 -0
- data/app/controllers/afterparty/dashboard_controller.rb +20 -0
- data/app/views/afterparty/dashboard/index.html.haml +66 -0
- data/config/routes.rb +9 -0
- data/lib/afterparty.rb +49 -0
- data/lib/afterparty/engine.rb +7 -0
- data/lib/afterparty/job_container.rb +36 -0
- data/lib/afterparty/queue_helpers.rb +33 -4
- data/lib/afterparty/redis_queue.rb +16 -6
- data/lib/afterparty/version.rb +1 -1
- data/lib/afterparty/worker.rb +56 -0
- data/lib/tasks/tasks.rake +25 -0
- data/spec/redis_queue_spec.rb +23 -16
- metadata +25 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            !binary "U0hBMQ==":
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b61c1a12ac1be3fb98d8615c4126689032529792
         | 
| 4 | 
            +
              data.tar.gz: 9ee9c0fd733e0d96133040e956a021e0f3f5c356
         | 
| 5 5 | 
             
            !binary "U0hBNTEy":
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6d4e7ebafeff397b8aa751c60e8e1198a435701984be74d7f5275be86b55ac5cc41512db4846a42a81abecf57a5a56f9770554b39ef5fc9380570fb053e2e3f1
         | 
| 7 | 
            +
              data.tar.gz: c744c87dc122d7901d6ebcc9b758740c6c563aa01ff8a802e23a7f49aa68d1b198bafe2460da9ba826efd3640cefde72e8e5f047d7139ad22a16f670c919dffe
         | 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,6 +1,8 @@ | |
| 1 1 | 
             
            # Afterparty
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            [](https://travis-ci.org/hstove/afterparty)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            A Rails 4 compatible queue with support for executing jobs in the future and persistence with Redis.
         | 
| 4 6 |  | 
| 5 7 | 
             
            ## Installation
         | 
| 6 8 |  | 
| @@ -41,7 +43,7 @@ end | |
| 41 43 | 
             
            Then add it to the queue at any time.
         | 
| 42 44 |  | 
| 43 45 | 
             
            ~~~Ruby
         | 
| 44 | 
            -
            Rails.queue << Job.new
         | 
| 46 | 
            +
            Rails.configuration.queue << Job.new
         | 
| 45 47 | 
             
            ~~~
         | 
| 46 48 |  | 
| 47 49 | 
             
            If your job responds to an `execute_at` method, the queue will wait to process that job until the specified time.
         | 
    
        data/afterparty.gemspec
    CHANGED
    
    
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            $(document).ready ->
         | 
| 2 | 
            +
              # $('.debug').click (e) ->
         | 
| 3 | 
            +
              #   $el = $(e.target)
         | 
| 4 | 
            +
              #   $tr = $el.parents('tr')
         | 
| 5 | 
            +
              #   $tr.next().toggle()
         | 
| 6 | 
            +
              #   $tr.toggleClass('debugged')
         | 
| 7 | 
            +
              #   false
         | 
| 8 | 
            +
              $('.job-row').click (e) ->
         | 
| 9 | 
            +
                $el = $(e.target)
         | 
| 10 | 
            +
                return true if $el.hasClass('job-action')
         | 
| 11 | 
            +
                $tr = $el.parents('tr')
         | 
| 12 | 
            +
                $tr.next().toggle()
         | 
| 13 | 
            +
                $tr.toggleClass('debugged')
         | 
| 14 | 
            +
                false
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            body
         | 
| 2 | 
            +
              :font-family helvetica
         | 
| 3 | 
            +
              :background-color whitesmoke
         | 
| 4 | 
            +
              :color lighten(black,30%)
         | 
| 5 | 
            +
              :margin 0 20px
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            a:visited, a
         | 
| 8 | 
            +
              :color rgb(96, 89, 180)
         | 
| 9 | 
            +
              :text-decoration none
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            span
         | 
| 12 | 
            +
              :font-size 12px
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            .job-table
         | 
| 15 | 
            +
              :border-spacing 0
         | 
| 16 | 
            +
              :max-width 100%
         | 
| 17 | 
            +
              :margin 0px auto
         | 
| 18 | 
            +
              thead tr:first-child
         | 
| 19 | 
            +
                th
         | 
| 20 | 
            +
                  :border-width 1px 1px 1px 0
         | 
| 21 | 
            +
                  &:first-child
         | 
| 22 | 
            +
                    :border-width 1px 1px 1px 1px
         | 
| 23 | 
            +
                th:first-child
         | 
| 24 | 
            +
                  :border-top-left-radius 2px
         | 
| 25 | 
            +
                th:last-child
         | 
| 26 | 
            +
                  :border-top-right-radius 2px
         | 
| 27 | 
            +
              tbody tr:last-child
         | 
| 28 | 
            +
                td:first-child
         | 
| 29 | 
            +
                  :border-bottom-left-radius 2px
         | 
| 30 | 
            +
                td:last-child
         | 
| 31 | 
            +
                  :border-bottom-right-radius 2px
         | 
| 32 | 
            +
              tr
         | 
| 33 | 
            +
                td, th
         | 
| 34 | 
            +
                  :border 1px solid #ccc
         | 
| 35 | 
            +
                  :border-width 0 1px 1px 0
         | 
| 36 | 
            +
                  :background-color lighten(whitesmoke, 5%)
         | 
| 37 | 
            +
                  :padding 12px 5px
         | 
| 38 | 
            +
                  :text-align left
         | 
| 39 | 
            +
                  :vertical-align middle
         | 
| 40 | 
            +
                  :word-wrap break-word
         | 
| 41 | 
            +
                  &:nth-child(2)
         | 
| 42 | 
            +
                    :width 70%
         | 
| 43 | 
            +
                  &:first-child
         | 
| 44 | 
            +
                    :border-width 0 1px 1px 1px
         | 
| 45 | 
            +
                  &:first-child, &:last-child
         | 
| 46 | 
            +
                    :width 15% !important
         | 
| 47 | 
            +
                &.debugged
         | 
| 48 | 
            +
                  td
         | 
| 49 | 
            +
                    :background-color rgba(255,255,0,0.25)
         | 
| 50 | 
            +
                &.job-row
         | 
| 51 | 
            +
                  :cursor pointer
         | 
| 52 | 
            +
                  h3
         | 
| 53 | 
            +
                    :margin 0
         | 
| 54 | 
            +
                  &:hover
         | 
| 55 | 
            +
                    td
         | 
| 56 | 
            +
                      :background-color rgba(0,0,0,0.15)
         | 
| 57 | 
            +
                    .description
         | 
| 58 | 
            +
                      :color white
         | 
| 59 | 
            +
              .debug-row
         | 
| 60 | 
            +
                :display none
         | 
| 61 | 
            +
                :max-width 100%
         | 
| 62 | 
            +
                td pre
         | 
| 63 | 
            +
                  :word-wrap break-word
         | 
| 64 | 
            +
                  :white-space pre-wrap 
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            .distance-past, .distance-future
         | 
| 67 | 
            +
              :font-size 12px
         | 
| 68 | 
            +
            .distance-future
         | 
| 69 | 
            +
              :color darken(rgb(0,255,0), 20%)
         | 
| 70 | 
            +
            .distance-past
         | 
| 71 | 
            +
              :color rgba(255,0,0,0.6)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            .description
         | 
| 74 | 
            +
              :font-size 12px
         | 
| 75 | 
            +
              :color #bbb
         | 
| 76 | 
            +
              :margin-left 10px
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            .job-action
         | 
| 79 | 
            +
              :text-decoration underline
         | 
| 80 | 
            +
              :font-size 12px
         | 
| 81 | 
            +
              :margin 0 2px
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            .job_id
         | 
| 84 | 
            +
              :color #bbb
         | 
| 85 | 
            +
              :font-weight 700
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            module Afterparty
         | 
| 2 | 
            +
              class DashboardController < ApplicationController
         | 
| 3 | 
            +
                layout false
         | 
| 4 | 
            +
                def index
         | 
| 5 | 
            +
                  @queues = Afterparty.queues
         | 
| 6 | 
            +
                  if params[:completed]
         | 
| 7 | 
            +
                    @jobs = queue.completed_with_scores
         | 
| 8 | 
            +
                  else
         | 
| 9 | 
            +
                    @jobs = queue.jobs_with_scores
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def queue
         | 
| 14 | 
            +
                  Rails.configuration.queue
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def run
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            !!!
         | 
| 2 | 
            +
            %html
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              %head
         | 
| 5 | 
            +
                %title== #{Rails.application.class.parent_name} Job Queue
         | 
| 6 | 
            +
                %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}
         | 
| 7 | 
            +
                / %meta{name: "viewport", content: "width=device-width, initial-scale=1.0"}
         | 
| 8 | 
            +
                = stylesheet_link_tag "afterparty"
         | 
| 9 | 
            +
                = javascript_include_tag "jquery", "afterparty"
         | 
| 10 | 
            +
                = csrf_meta_tag
         | 
| 11 | 
            +
                = favicon_link_tag
         | 
| 12 | 
            +
                = yield(:head)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              %body
         | 
| 15 | 
            +
                %h1
         | 
| 16 | 
            +
                  Viewing
         | 
| 17 | 
            +
                  - if params[:completed]
         | 
| 18 | 
            +
                    = pluralize @jobs.size, "completed job"
         | 
| 19 | 
            +
                    %h3= link_to "View Job Queue", afterparty_engine.dashboard_path
         | 
| 20 | 
            +
                  - else
         | 
| 21 | 
            +
                    = pluralize @jobs.size, "job"
         | 
| 22 | 
            +
                    %h3= link_to "View Completed Jobs", afterparty_engine.dashboard_path(completed: true)
         | 
| 23 | 
            +
                = @queues
         | 
| 24 | 
            +
                %table.job-table
         | 
| 25 | 
            +
                  %thead
         | 
| 26 | 
            +
                    %tr
         | 
| 27 | 
            +
                      %th Execute At
         | 
| 28 | 
            +
                      / %th Job
         | 
| 29 | 
            +
                      / %th Actions
         | 
| 30 | 
            +
                  %tbody
         | 
| 31 | 
            +
                    - if @jobs.empty?
         | 
| 32 | 
            +
                      %tr
         | 
| 33 | 
            +
                        %td{colspan: 3}
         | 
| 34 | 
            +
                          %em No jobs to show...
         | 
| 35 | 
            +
                    - else
         | 
| 36 | 
            +
                      - @jobs.each do |job_container|
         | 
| 37 | 
            +
                        %tr.job-row
         | 
| 38 | 
            +
                          %td
         | 
| 39 | 
            +
                            %h3
         | 
| 40 | 
            +
                              - if job_container.job
         | 
| 41 | 
            +
                                = link_to job_container.job_class, "#", class: 'debug'
         | 
| 42 | 
            +
                                - if job_container.job.respond_to? :description
         | 
| 43 | 
            +
                                  %span.description= job_container.job.description
         | 
| 44 | 
            +
                              - else
         | 
| 45 | 
            +
                                %em Error marshaling job
         | 
| 46 | 
            +
                            =# job_container.execute_at.strftime("%B %d, %Y at %l:%M %P")
         | 
| 47 | 
            +
                            - distance = time_ago_in_words(job_container.execute_at)
         | 
| 48 | 
            +
                            - if job_container.execute_at > Time.now
         | 
| 49 | 
            +
                              %span.distance-future= "in #{distance}"
         | 
| 50 | 
            +
                            - else
         | 
| 51 | 
            +
                              %span.distance-past= "#{distance} ago"
         | 
| 52 | 
            +
                            - if (_id = job_container.job_id) && (_queue = job_container.queue_name)
         | 
| 53 | 
            +
                              %span.job_id== ##{_id} #{_queue}
         | 
| 54 | 
            +
                              - unless params[:completed]
         | 
| 55 | 
            +
                                = link_to "run", afterparty_engine.run_job_path(job_id: _id, queue: _queue), class: 'job-action'
         | 
| 56 | 
            +
                                = link_to "delete", afterparty_engine.delete_job_path(job_id: _id, queue: _queue), class: 'job-action'
         | 
| 57 | 
            +
                        %tr.debug-row
         | 
| 58 | 
            +
                          %td
         | 
| 59 | 
            +
                            - if job_container.job
         | 
| 60 | 
            +
                              = debug job_container.job
         | 
| 61 | 
            +
                            - else
         | 
| 62 | 
            +
                              = job_container.raw_string
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            %p
         | 
| 65 | 
            +
              Current Time:
         | 
| 66 | 
            +
              = Time.now.strftime("%B %d, %Y at %l:%M %P")
         | 
    
        data/config/routes.rb
    ADDED
    
    | @@ -0,0 +1,9 @@ | |
| 1 | 
            +
            Afterparty::Engine.routes.draw do
         | 
| 2 | 
            +
              get "/" => "afterparty/dashboard#index", as: :dashboard
         | 
| 3 | 
            +
              get "/run" => "afterparty/dashboard#run", as: :run_job
         | 
| 4 | 
            +
              get "/delete" => "afterparty/dashboard#run", as: :delete_job
         | 
| 5 | 
            +
            end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Rails.application.routes.draw do
         | 
| 8 | 
            +
              mount Afterparty::Engine, at: "afterparty", as: "afterparty_engine"
         | 
| 9 | 
            +
            end
         | 
    
        data/lib/afterparty.rb
    CHANGED
    
    | @@ -1,14 +1,63 @@ | |
| 1 1 | 
             
            require 'logger'
         | 
| 2 2 | 
             
            require 'afterparty/queue_helpers'
         | 
| 3 3 | 
             
            require 'afterparty/redis_queue'
         | 
| 4 | 
            +
            require 'redis'
         | 
| 4 5 | 
             
            Dir[File.expand_path('../afterparty/*', __FILE__)].each { |f| require f }
         | 
| 5 6 |  | 
| 6 7 |  | 
| 7 8 | 
             
            module Afterparty
         | 
| 9 | 
            +
              @@redis = Redis.new
         | 
| 10 | 
            +
             | 
| 8 11 | 
             
              def self.redis
         | 
| 9 12 | 
             
                @@redis
         | 
| 10 13 | 
             
              end
         | 
| 11 14 | 
             
              def self.redis=(redis)
         | 
| 12 15 | 
             
                @@redis = redis
         | 
| 13 16 | 
             
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def self.clear namespace=:default
         | 
| 19 | 
            +
                redis_call namespace, :del
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def self.redis_call namespace, command, *args
         | 
| 23 | 
            +
                @@redis.send(command, redis_queue_name(namespace), *args)
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def self.redis_queue_name namespace=:default
         | 
| 27 | 
            +
                "afterparty_#{namespace}_queue"
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def self.queues
         | 
| 31 | 
            +
                @@redis.smembers "afterparty_queues"
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def self.add_queue name
         | 
| 35 | 
            +
                @@redis.sadd "afterparty_queues", name
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def self.next_job_id namespace=:default
         | 
| 39 | 
            +
                @@redis.incr "afterparty_#{namespace.to_s}_job_id"
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def self.load(raw)
         | 
| 43 | 
            +
                begin
         | 
| 44 | 
            +
                  begin
         | 
| 45 | 
            +
                    job = Marshal.load(raw)
         | 
| 46 | 
            +
                    job = Marshal.load(job) if String === job
         | 
| 47 | 
            +
                    return job
         | 
| 48 | 
            +
                  rescue NameError => e
         | 
| 49 | 
            +
                    # lots of marshal load errors are because something that hasn't been
         | 
| 50 | 
            +
                    # required. recursively require on these errors
         | 
| 51 | 
            +
                    name = e.message.gsub("uninitialized constant ","").downcase
         | 
| 52 | 
            +
                    begin
         | 
| 53 | 
            +
                      require "#{name}"
         | 
| 54 | 
            +
                      return load(raw)
         | 
| 55 | 
            +
                    rescue LoadError
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                rescue
         | 
| 59 | 
            +
                  return nil
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 14 63 | 
             
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'iconv'
         | 
| 2 | 
            +
            require 'date'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Afterparty
         | 
| 5 | 
            +
              class JobContainer
         | 
| 6 | 
            +
                attr_accessor :job, :raw, :execute_at, :job_id, :queue_name
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                #intialized from redis's WITHSCORES function
         | 
| 9 | 
            +
                def initialize _raw, timestamp
         | 
| 10 | 
            +
                  @execute_at = Time.at(timestamp)
         | 
| 11 | 
            +
                  begin
         | 
| 12 | 
            +
                    @job = Afterparty.load(_raw)
         | 
| 13 | 
            +
                    @job_id = job.afterparty_job_id if @job.respond_to? :afterparty_job_id
         | 
| 14 | 
            +
                    @queue_name = job.afterparty_queue if @job.respond_to? :afterparty_queue
         | 
| 15 | 
            +
                  rescue Exception => e
         | 
| 16 | 
            +
                    ap "Error during load: #{e.message}"
         | 
| 17 | 
            +
                    @job = nil
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                  @raw = _raw
         | 
| 20 | 
            +
                  self
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def job_class
         | 
| 24 | 
            +
                  if @job
         | 
| 25 | 
            +
                    @job.class
         | 
| 26 | 
            +
                  else
         | 
| 27 | 
            +
                    nil
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def raw_string
         | 
| 32 | 
            +
                  ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
         | 
| 33 | 
            +
                  ic.iconv(@raw.dup + ' ')[0..-2]
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -4,8 +4,9 @@ module Afterparty | |
| 4 4 | 
             
                  @temp_namespace = namespace
         | 
| 5 5 | 
             
                end
         | 
| 6 6 |  | 
| 7 | 
            -
                def redis_queue_name
         | 
| 8 | 
            -
                   | 
| 7 | 
            +
                def redis_queue_name  
         | 
| 8 | 
            +
                  puts (a = Afterparty.redis_queue_name(@temp_namespace || @options[:namespace]))
         | 
| 9 | 
            +
                  a
         | 
| 9 10 | 
             
                end
         | 
| 10 11 |  | 
| 11 12 | 
             
                def clear
         | 
| @@ -13,7 +14,7 @@ module Afterparty | |
| 13 14 | 
             
                end
         | 
| 14 15 |  | 
| 15 16 | 
             
                def redis_call command, *args
         | 
| 16 | 
            -
                  result = Afterparty. | 
| 17 | 
            +
                  result = Afterparty.redis_call (@temp_namespace || @options[:namespace]), command, *args
         | 
| 17 18 | 
             
                  @temp_namespace = nil
         | 
| 18 19 | 
             
                  result
         | 
| 19 20 | 
             
                end
         | 
| @@ -31,13 +32,17 @@ module Afterparty | |
| 31 32 | 
             
                end
         | 
| 32 33 |  | 
| 33 34 | 
             
                def jobs_with_scores
         | 
| 34 | 
            -
                  redis_call | 
| 35 | 
            +
                  hash_from_scores(redis_call(:zrange, 0, -1, {withscores: true}))
         | 
| 35 36 | 
             
                end
         | 
| 36 37 |  | 
| 37 38 | 
             
                def valid_jobs
         | 
| 38 39 | 
             
                  redis_call :zrangebyscore, 0, Time.now.to_i
         | 
| 39 40 | 
             
                end
         | 
| 40 41 |  | 
| 42 | 
            +
                def next_valid_job
         | 
| 43 | 
            +
                  valid_jobs.first
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 41 46 | 
             
                def jobs_empty?
         | 
| 42 47 | 
             
                  count = total_jobs_count
         | 
| 43 48 | 
             
                  # ap count
         | 
| @@ -52,8 +57,32 @@ module Afterparty | |
| 52 57 | 
             
                  @@redis
         | 
| 53 58 | 
             
                end
         | 
| 54 59 |  | 
| 60 | 
            +
                def last_completed
         | 
| 61 | 
            +
                  @temp_namespace = "completed"
         | 
| 62 | 
            +
                  redis_call(:zrange, -1, -1).first
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def completed
         | 
| 66 | 
            +
                  @temp_namespace = "completed"
         | 
| 67 | 
            +
                  redis_call(:zrange, -20, -1).reverse
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def completed_with_scores
         | 
| 71 | 
            +
                  @temp_namespace = "completed"
         | 
| 72 | 
            +
                  hash_from_scores(redis_call(:zrange, -20, -1, withscores: true)).reverse
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
             | 
| 55 76 | 
             
                private
         | 
| 56 77 |  | 
| 78 | 
            +
                def hash_from_scores raw
         | 
| 79 | 
            +
                  arr = []
         | 
| 80 | 
            +
                  raw.each do |group|
         | 
| 81 | 
            +
                    arr << Afterparty::JobContainer.new(group[0], group[1])
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                  arr
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 57 86 | 
             
                # returns true if job has an :execute_at value
         | 
| 58 87 | 
             
                def job_valid? job
         | 
| 59 88 | 
             
                  job.respond_to?(:execute_at) && !job.execute_at.nil?
         | 
| @@ -4,9 +4,10 @@ module Afterparty | |
| 4 4 | 
             
                include Afterparty::QueueHelpers
         | 
| 5 5 |  | 
| 6 6 | 
             
                def initialize options={}, consumer_options={}
         | 
| 7 | 
            -
                  @consumer = ThreadedQueueConsumer.new(self, consumer_options).start
         | 
| 7 | 
            +
                  # @consumer = ThreadedQueueConsumer.new(self, consumer_options).start
         | 
| 8 8 | 
             
                  @options = options
         | 
| 9 9 | 
             
                  @options[:namespace] ||= "default"
         | 
| 10 | 
            +
                  Afterparty.add_queue @options[:namespace]
         | 
| 10 11 | 
             
                  @options[:sleep] ||= 5
         | 
| 11 12 | 
             
                  @mutex = Mutex.new
         | 
| 12 13 | 
             
                end
         | 
| @@ -14,8 +15,13 @@ module Afterparty | |
| 14 15 | 
             
                def push job
         | 
| 15 16 | 
             
                  @mutex.synchronize do
         | 
| 16 17 | 
             
                    return nil if job.nil?
         | 
| 18 | 
            +
                    job.class.module_eval do
         | 
| 19 | 
            +
                      attr_accessor :afterparty_job_id, :afterparty_queue
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    queue_name = @temp_namespace || @options[:namespace]
         | 
| 22 | 
            +
                    job.afterparty_queue = queue_name
         | 
| 23 | 
            +
                    job.afterparty_job_id = Afterparty.next_job_id queue_name
         | 
| 17 24 | 
             
                    async_redis_call{ redis_call :zadd, queue_time(job), Marshal.dump(job) }
         | 
| 18 | 
            -
                    @consumer.start unless @consumer.thread.alive?
         | 
| 19 25 | 
             
                    @temp_namespace = nil
         | 
| 20 26 | 
             
                  end
         | 
| 21 27 | 
             
                end
         | 
| @@ -25,16 +31,20 @@ module Afterparty | |
| 25 31 | 
             
                def pop
         | 
| 26 32 | 
             
                  @mutex.synchronize do
         | 
| 27 33 | 
             
                    while true do
         | 
| 28 | 
            -
                      if  | 
| 29 | 
            -
                        @consumer.shutdown
         | 
| 30 | 
            -
                      elsif !(_jobs = valid_jobs).empty?
         | 
| 34 | 
            +
                      if !(_jobs = valid_jobs).empty?
         | 
| 31 35 | 
             
                        job_dump = _jobs[0]
         | 
| 32 36 | 
             
                        async_redis_call do
         | 
| 33 37 | 
             
                          redis_call :zrem, job_dump
         | 
| 34 38 | 
             
                          @temp_namespace = "completed"
         | 
| 35 39 | 
             
                          redis_call :zadd, Time.now.to_i, job_dump
         | 
| 36 40 | 
             
                        end
         | 
| 37 | 
            -
                         | 
| 41 | 
            +
                        begin
         | 
| 42 | 
            +
                          return Marshal.load(job_dump)
         | 
| 43 | 
            +
                        rescue ArgumentException => e
         | 
| 44 | 
            +
                          puts "You encountered an argument exception while deserializing a job."
         | 
| 45 | 
            +
                          puts "Message: #{e.message}"
         | 
| 46 | 
            +
                          raise e
         | 
| 47 | 
            +
                        end
         | 
| 38 48 | 
             
                      end
         | 
| 39 49 | 
             
                      sleep(@options[:sleep])
         | 
| 40 50 | 
             
                    end
         | 
    
        data/lib/afterparty/version.rb
    CHANGED
    
    
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            module Afterparty
         | 
| 2 | 
            +
              class Worker
         | 
| 3 | 
            +
                include QueueHelpers
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize options = {}
         | 
| 6 | 
            +
                  @options = options
         | 
| 7 | 
            +
                  @options[:adapter] ||= :redis
         | 
| 8 | 
            +
                  @options[:namespace] ||= :default
         | 
| 9 | 
            +
                  @options[:sleep] ||= 10
         | 
| 10 | 
            +
                  @options[:logger] ||= Logger.new($stderr)
         | 
| 11 | 
            +
                  self
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def consume
         | 
| 15 | 
            +
                  @stopped = false
         | 
| 16 | 
            +
                  # puts "starting worker with namespace [#{@options[:namespace]}]."
         | 
| 17 | 
            +
                  @thread = Thread.new {
         | 
| 18 | 
            +
                    consume_sync
         | 
| 19 | 
            +
                  }
         | 
| 20 | 
            +
                  @thread
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def consume_sync
         | 
| 24 | 
            +
                  while !@stopped
         | 
| 25 | 
            +
                    job = next_valid_job
         | 
| 26 | 
            +
                    if job
         | 
| 27 | 
            +
                      async_redis_call do
         | 
| 28 | 
            +
                        @temp_namespace = "completed"
         | 
| 29 | 
            +
                        redis_call :zadd, Time.now.to_i, Marshal.dump(job)
         | 
| 30 | 
            +
                        redis_call :zrem, job
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                      run job
         | 
| 33 | 
            +
                    else
         | 
| 34 | 
            +
                      sleep(@options[:sleep])
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def stop
         | 
| 40 | 
            +
                  @stopped = true
         | 
| 41 | 
            +
                  @thread.join(0)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def run(job)
         | 
| 45 | 
            +
                  fork do
         | 
| 46 | 
            +
                    Marshal.load(job).run
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                rescue Exception => exception
         | 
| 49 | 
            +
                  handle_exception job, exception
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def handle_exception(job, exception)
         | 
| 53 | 
            +
                  @options[:logger].error "Job Error: #{job.inspect}\n#{exception.message}\n#{exception.backtrace.join("\n")}"
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            namespace :jobs do
         | 
| 2 | 
            +
              require 'mail'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              desc "Start a new worker"
         | 
| 5 | 
            +
              task work: :environment do
         | 
| 6 | 
            +
                worker = Afterparty::Worker.new
         | 
| 7 | 
            +
                worker.consume_sync
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              desc "Clear all jobs"
         | 
| 11 | 
            +
              task clear: :environment do
         | 
| 12 | 
            +
                Rails.configuration.queue.clear
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              desc "List Jobs"
         | 
| 16 | 
            +
              task list: :environment do
         | 
| 17 | 
            +
                jobs = Rails.configuration.queue.jobs_with_scores
         | 
| 18 | 
            +
                puts "#{jobs.values.size} total jobs."
         | 
| 19 | 
            +
                jobs.each do |time, job|
         | 
| 20 | 
            +
                  puts time
         | 
| 21 | 
            +
                  puts job
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| 25 | 
            +
             | 
    
        data/spec/redis_queue_spec.rb
    CHANGED
    
    | @@ -8,10 +8,16 @@ describe Afterparty::RedisQueue do | |
| 8 8 | 
             
                @q = Afterparty::TestRedisQueue.new({sleep: 0.5})
         | 
| 9 9 | 
             
              end
         | 
| 10 10 |  | 
| 11 | 
            +
              after do
         | 
| 12 | 
            +
                @worker.stop
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 11 15 | 
             
              before :each do
         | 
| 12 | 
            -
                @ | 
| 16 | 
            +
                @worker = Afterparty::Worker.new({sleep: 0.5})
         | 
| 17 | 
            +
                @worker.consume
         | 
| 13 18 | 
             
                @q.clear
         | 
| 14 | 
            -
                 | 
| 19 | 
            +
                @job_time = (ENV['AFTERPARTY_JOB_TIME'] || 3).to_i
         | 
| 20 | 
            +
                @slow_job_time = (ENV['AFTERPARTY_SLOW_TIME'] || 10).to_i
         | 
| 15 21 | 
             
              end
         | 
| 16 22 |  | 
| 17 23 | 
             
              it "pushes nil without errors" do
         | 
| @@ -27,34 +33,35 @@ describe Afterparty::RedisQueue do | |
| 27 33 | 
             
              it "executes the job" do
         | 
| 28 34 | 
             
                job = TestJob.new
         | 
| 29 35 | 
             
                @q.push(job)
         | 
| 30 | 
            -
                 | 
| 31 | 
            -
                chill( | 
| 32 | 
            -
                 | 
| 36 | 
            +
                @q.jobs.size.should eq(1)
         | 
| 37 | 
            +
                chill(@job_time)
         | 
| 38 | 
            +
                @q.jobs.size.should eq(0)
         | 
| 33 39 | 
             
              end
         | 
| 34 40 |  | 
| 35 41 | 
             
              it "removes items from the queue after running them" do
         | 
| 36 42 | 
             
                @q.push TestJob.new
         | 
| 37 | 
            -
                chill( | 
| 38 | 
            -
                @q.jobs. | 
| 43 | 
            +
                chill(@job_time)
         | 
| 44 | 
            +
                @q.jobs.size.should == 0
         | 
| 39 45 | 
             
              end
         | 
| 40 46 |  | 
| 41 47 | 
             
              it "doesn't execute jobs that execute in a while" do
         | 
| 42 48 | 
             
                job = TestJob.new
         | 
| 43 49 | 
             
                job.execute_at = Time.now + 200
         | 
| 44 50 | 
             
                @q.push job
         | 
| 45 | 
            -
                chill( | 
| 46 | 
            -
                 | 
| 51 | 
            +
                chill(@job_time)
         | 
| 52 | 
            +
                @q.jobs.size.should eq(1)
         | 
| 47 53 | 
             
              end
         | 
| 48 54 |  | 
| 49 55 | 
             
              it "waits the correct amount of time to execute a job" do
         | 
| 50 56 | 
             
                job = TestJob.new
         | 
| 51 | 
            -
                job.execute_at = Time.now +  | 
| 57 | 
            +
                job.execute_at = Time.now + 5
         | 
| 52 58 | 
             
                @q.push(job)
         | 
| 53 | 
            -
                 | 
| 54 | 
            -
                 | 
| 59 | 
            +
                @q.jobs.size.should eq(1)
         | 
| 60 | 
            +
                chill(@slow_job_time)
         | 
| 61 | 
            +
                @q.jobs.size.should eq(0)
         | 
| 55 62 | 
             
              end
         | 
| 56 63 |  | 
| 57 | 
            -
              it "doesn't execute the job synchronously when added" do
         | 
| 64 | 
            +
              it "doesn't wait and execute the job synchronously when added" do
         | 
| 58 65 | 
             
                job = test_job 100
         | 
| 59 66 | 
             
                t = Time.now
         | 
| 60 67 | 
             
                @q.push(job)
         | 
| @@ -66,9 +73,9 @@ describe Afterparty::RedisQueue do | |
| 66 73 | 
             
                early_job = test_job
         | 
| 67 74 | 
             
                @q.push(late_job)
         | 
| 68 75 | 
             
                @q.push(early_job)
         | 
| 69 | 
            -
                chill( | 
| 70 | 
            -
                 | 
| 71 | 
            -
                 | 
| 76 | 
            +
                chill(@job_time)
         | 
| 77 | 
            +
                (jobs = @q.jobs).size.should eq(1)
         | 
| 78 | 
            +
                jobs[0].execute_at.should_not be(nil)
         | 
| 72 79 | 
             
              end
         | 
| 73 80 |  | 
| 74 81 | 
             
              class ErrorJob
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: afterparty
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Hank Stoever
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2013-05- | 
| 11 | 
            +
            date: 2013-05-15 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: redis
         | 
| @@ -24,6 +24,20 @@ dependencies: | |
| 24 24 | 
             
                - - ! '>='
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: iconv
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ! '>='
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ! '>='
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0'
         | 
| 27 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 42 | 
             
              name: bundler
         | 
| 29 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -68,12 +82,21 @@ files: | |
| 68 82 | 
             
            - README.md
         | 
| 69 83 | 
             
            - Rakefile
         | 
| 70 84 | 
             
            - afterparty.gemspec
         | 
| 85 | 
            +
            - app/assets/javascripts/afterparty.js.coffee
         | 
| 86 | 
            +
            - app/assets/stylesheets/afterparty.css.sass
         | 
| 87 | 
            +
            - app/controllers/afterparty/dashboard_controller.rb
         | 
| 88 | 
            +
            - app/views/afterparty/dashboard/index.html.haml
         | 
| 89 | 
            +
            - config/routes.rb
         | 
| 71 90 | 
             
            - dump.rdb
         | 
| 72 91 | 
             
            - lib/afterparty.rb
         | 
| 92 | 
            +
            - lib/afterparty/engine.rb
         | 
| 93 | 
            +
            - lib/afterparty/job_container.rb
         | 
| 73 94 | 
             
            - lib/afterparty/queue_helpers.rb
         | 
| 74 95 | 
             
            - lib/afterparty/redis_queue.rb
         | 
| 75 96 | 
             
            - lib/afterparty/threaded_queue_consumer.rb
         | 
| 76 97 | 
             
            - lib/afterparty/version.rb
         | 
| 98 | 
            +
            - lib/afterparty/worker.rb
         | 
| 99 | 
            +
            - lib/tasks/tasks.rake
         | 
| 77 100 | 
             
            - spec/helpers.rb
         | 
| 78 101 | 
             
            - spec/redis_queue_spec.rb
         | 
| 79 102 | 
             
            - spec/spec_helper.rb
         |