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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
- metadata.gz: d5a18d9460144527504869b72c7a377ce2c6eda0
4
- data.tar.gz: 4d9b52a88c5fea21d37da73b1a13f5f5bf180c9e
3
+ metadata.gz: b61c1a12ac1be3fb98d8615c4126689032529792
4
+ data.tar.gz: 9ee9c0fd733e0d96133040e956a021e0f3f5c356
5
5
  !binary "U0hBNTEy":
6
- metadata.gz: d6323eb6eb7a72b1090207d498ae02890dbfca347bdc37414453f67441725e966b4205651d53d4756e15ce7a786ff1a1058c98572439c67e931ab0f150ce70c7
7
- data.tar.gz: 1888cd7d556ee3f5164935ac9fb7a9b57dcf9fb1808ff8eae57315bd8175f3080e2a1dca599437bbf87770f11acc48b598fd62a1272a4c8077f0c7385304d9a1
6
+ metadata.gz: 6d4e7ebafeff397b8aa751c60e8e1198a435701984be74d7f5275be86b55ac5cc41512db4846a42a81abecf57a5a56f9770554b39ef5fc9380570fb053e2e3f1
7
+ data.tar.gz: c744c87dc122d7901d6ebcc9b758740c6c563aa01ff8a802e23a7f49aa68d1b198bafe2460da9ba826efd3640cefde72e8e5f047d7139ad22a16f670c919dffe
@@ -3,4 +3,6 @@ rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
5
  services:
6
- - redis-server
6
+ - redis-server
7
+ env:
8
+ - AFTERPARTY_JOB_TIME=15 AFTERPARTY_SLOW_TIME=50
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Afterparty
2
2
 
3
- A Rails 4 compatible queue with support for executing jobs in the future and serialization with Redis.
3
+ [![Build Status](https://travis-ci.org/hstove/afterparty.png?branch=master)](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.
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "redis"
22
+ spec.add_dependency "iconv"
22
23
 
23
24
  spec.add_development_dependency "bundler", "~> 1.3"
24
25
  spec.add_development_dependency "rake"
@@ -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")
@@ -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
@@ -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,7 @@
1
+ require 'rails'
2
+ module Afterparty
3
+ class Engine < Rails::Engine
4
+ engine_name :afterparty
5
+
6
+ end
7
+ 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
- "afterparty_#{@temp_namespace || @options[:namespace]}_queue"
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.redis.send(command, redis_queue_name, *args)
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 :zrange, 0, -1, {withscores: true}
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 jobs_empty?
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
- return Marshal.load(job_dump)
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
@@ -1,3 +1,3 @@
1
1
  module Afterparty
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
+
@@ -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
- @q.completed_jobs.clear
16
+ @worker = Afterparty::Worker.new({sleep: 0.5})
17
+ @worker.consume
13
18
  @q.clear
14
- Afterparty.redis.quit
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
- complete.size.should eq(0)
31
- chill(3)
32
- complete.size.should eq(1)
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(1)
38
- @q.jobs.should_not include(@job)
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(3)
46
- complete.size.should eq(0)
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 + 2
57
+ job.execute_at = Time.now + 5
52
58
  @q.push(job)
53
- chill(8)
54
- complete.size.should eq(1)
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(3)
70
- complete.size.should eq(1)
71
- complete[0].execute_at.should be(nil)
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.2
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-14 00:00:00.000000000 Z
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