pvdgm_beanstalk_api 0.1.0

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +9 -0
  3. data/Rakefile +38 -0
  4. data/app/controllers/beanstalk_api/application_controller.rb +68 -0
  5. data/app/controllers/beanstalk_api/jobs_controller.rb +27 -0
  6. data/app/controllers/beanstalk_api/tasks_controller.rb +16 -0
  7. data/app/controllers/beanstalk_api/tubes_controller.rb +149 -0
  8. data/app/helpers/beanstalk_api/application_helper.rb +4 -0
  9. data/app/views/layouts/beanstalk_api/application.html.erb +14 -0
  10. data/config/routes.rb +19 -0
  11. data/lib/beanstalk_api.rb +4 -0
  12. data/lib/beanstalk_api/engine.rb +11 -0
  13. data/lib/beanstalk_api/version.rb +3 -0
  14. data/lib/tasks/beanstalk_api_tasks.rake +4 -0
  15. data/spec/controllers/application_controller_spec.rb +87 -0
  16. data/spec/controllers/jobs_controller_spec.rb +35 -0
  17. data/spec/dummy/README.rdoc +261 -0
  18. data/spec/dummy/Rakefile +7 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  20. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  21. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  22. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  23. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/spec/dummy/config.ru +4 -0
  25. data/spec/dummy/config/application.rb +65 -0
  26. data/spec/dummy/config/boot.rb +10 -0
  27. data/spec/dummy/config/database.yml +29 -0
  28. data/spec/dummy/config/environment.rb +5 -0
  29. data/spec/dummy/config/environments/development.rb +38 -0
  30. data/spec/dummy/config/environments/production.rb +68 -0
  31. data/spec/dummy/config/environments/test.rb +38 -0
  32. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  33. data/spec/dummy/config/initializers/inflections.rb +15 -0
  34. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  35. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  36. data/spec/dummy/config/initializers/session_store.rb +8 -0
  37. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  38. data/spec/dummy/config/locales/en.yml +5 -0
  39. data/spec/dummy/config/routes.rb +5 -0
  40. data/spec/dummy/db/schema.rb +121 -0
  41. data/spec/dummy/public/404.html +26 -0
  42. data/spec/dummy/public/422.html +26 -0
  43. data/spec/dummy/public/500.html +25 -0
  44. data/spec/dummy/public/favicon.ico +0 -0
  45. data/spec/dummy/script/rails +6 -0
  46. data/spec/spec_helper.rb +40 -0
  47. metadata +205 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b885363a94242b1a19e7eada2e42aa05a9177b29
4
+ data.tar.gz: 0d4522c3003e3e923e538c4f3bb5a02f660b95b1
5
+ SHA512:
6
+ metadata.gz: 35ee80c8f48e2f94be681e489f7752f71a7f5f39d8d99b128f2640119f78ba2f27bfd5b43d0f004d0b63ba06dbc9a4c438ed2c8f5687b41fd400a8f5ee92184a
7
+ data.tar.gz: be7d15b7e2f0b46d2c410f117f3f8549d836a2b308b5238be459cf2d98a589dbb33de90da13f56453afef26c6c0bd0a8fe5b7704ddbe569d549888ebc0646db7
@@ -0,0 +1,9 @@
1
+ BeanstalkApi
2
+ ============
3
+
4
+ This engine provides the RESTful apis to discover information
5
+ about a connected beanstalk environment.
6
+
7
+ You can add this engine to any Rails application that uses beanstalkd.
8
+
9
+ Enjoy.
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
9
+ load 'rails/tasks/engine.rake'
10
+
11
+ begin
12
+ require 'rdoc/task'
13
+ rescue LoadError
14
+ require 'rdoc/rdoc'
15
+ require 'rake/rdoctask'
16
+ RDoc::Task = Rake::RDocTask
17
+ end
18
+
19
+ RDoc::Task.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'BeanstalkApi'
22
+ rdoc.options << '--line-numbers'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+
28
+
29
+
30
+ Bundler::GemHelper.install_tasks
31
+
32
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
33
+
34
+ require 'rspec/core'
35
+ require 'rspec/core/rake_task'
36
+ desc "Run all specs in spec directory (excluding plugin specs)"
37
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
38
+ task :default => :spec
@@ -0,0 +1,68 @@
1
+ module BeanstalkApi
2
+
3
+ class ApplicationController < ActionController::Base
4
+ before_filter :restrict_access
5
+
6
+ def authenticated_entity
7
+ @api_key.try(:authenticateable)
8
+ end
9
+
10
+ private
11
+
12
+ #
13
+ # This is different from the standard API authentication because we need to
14
+ # restrict access to only the API key that is associated with the providigm account.
15
+ #
16
+ def restrict_access
17
+ authorization_header = request.headers['Authorization'] || ''
18
+
19
+ if authorization_header =~ /^Basic/
20
+ authenticate_or_request_with_http_basic do | username, password |
21
+ @api_key = ApiKey.where(access_token: password).first
22
+ @api_key.present? && @api_key.authenticateable.id.to_s == username && username == '1'
23
+ end
24
+ else
25
+ authenticate_or_request_with_http_token do |token, options|
26
+ @api_key = ApiKey.where(access_token: token).first
27
+ @api_key.present? && @api_key.authenticateable.id.to_s == '1'
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def format_job(job)
35
+ hash = {}
36
+ if job.is_a?(Hash)
37
+ job.each_pair do | host, job |
38
+ hash[job.id] = job.body
39
+ end
40
+ else
41
+ hash[job.id] = job.body
42
+ end
43
+ hash
44
+ end
45
+
46
+ def beanstalk_client
47
+ Ayl::Engine.get_active_engine
48
+ end
49
+
50
+ def pool
51
+ beanstalk_client.pool
52
+ end
53
+
54
+ def connection
55
+ pool.open_connections.first
56
+ end
57
+
58
+ def beanstalk_running
59
+ render json: { error: "Beanstalk not running" } unless beanstalk_client.asynchronous?
60
+ end
61
+
62
+ def default_tube
63
+ defined?(ASYNC_TUBE) ? ASYNC_TUBE : 'default'
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,27 @@
1
+ module BeanstalkApi
2
+
3
+ class JobsController < BeanstalkApi::ApplicationController
4
+ attr_reader :job
5
+
6
+ before_filter :must_have_job
7
+
8
+ def show
9
+ render json: { job: format_job(job), statistics: connection.job_stats(params[:id]) }
10
+ end
11
+
12
+ def destroy
13
+ connection.delete(params[:id])
14
+ render json: { job: "Job '#{params[:id]}' Deleted" }
15
+ end
16
+
17
+ private
18
+
19
+ def must_have_job
20
+ @job = pool.peek_job(params[:id])
21
+ rescue Beanstalk::NotFoundError
22
+ render json: { error: "Job '#{params[:id]}' not found" }
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,16 @@
1
+ module BeanstalkApi
2
+
3
+ class TasksController < BeanstalkApi::ApplicationController
4
+
5
+ before_filter :beanstalk_running
6
+
7
+ respond_to :json
8
+
9
+ def overall_statistics
10
+ render json: pool.stats
11
+ end
12
+
13
+ end
14
+
15
+
16
+ end
@@ -0,0 +1,149 @@
1
+ module BeanstalkApi
2
+
3
+ class TubesController < BeanstalkApi::ApplicationController
4
+ attr_reader :tube
5
+
6
+ before_filter :must_have_existing_tube, only: [ :show, :ready, :delayed, :buried, :worker, :kick ]
7
+ before_filter :must_have_tube, only: [ :one_job, :all_jobs ]
8
+
9
+ respond_to :json
10
+
11
+ def index
12
+ tubes = pool.list_tubes
13
+
14
+ render json: tubes.values.flatten
15
+ end
16
+
17
+ def show
18
+ render json: pool.stats_tube(tube)
19
+ end
20
+
21
+ def ready
22
+ pool.use(tube)
23
+
24
+ job = pool.peek_ready
25
+
26
+ render json: { error: "No job in ready state on '#{tube}' tube" } and return if job.nil?
27
+
28
+ render json: format_job(job)
29
+ end
30
+
31
+ def delayed
32
+ pool.use(tube)
33
+
34
+ job = pool.peek_delayed
35
+
36
+ render json: { error: "No job in delayed state on '#{tube}' tube" } and return if job.nil?
37
+
38
+ render json: format_job(job)
39
+ end
40
+
41
+ def buried
42
+ pool.use(tube)
43
+
44
+ job = pool.peek_buried
45
+
46
+ render json: { error: "No job in buried state on '#{tube}' tube" } and return if job.nil?
47
+
48
+ render json: format_job(job)
49
+ end
50
+
51
+ def one_job
52
+ pool.watch(tube)
53
+
54
+ job = pool.reserve(0)
55
+ job.delete if job.present?
56
+
57
+ render json: { eat_job: "1 job deleted" }
58
+ rescue Beanstalk::TimedOut
59
+ render json: { eat_job: "No job to eat on tube '#{tube}'" }
60
+ end
61
+
62
+ def all_jobs
63
+ count = 0
64
+
65
+ pool.watch(tube)
66
+
67
+ while ((job = pool.reserve(0)).present?)
68
+ count += 1
69
+ job.delete
70
+ end
71
+
72
+ # So, be aware that under ALL circumstances the loop will end with a
73
+ # Beanstalk::TimedOut exception. We simply catch it and report the
74
+ # number of jobs eaten. There is no case where this method will not
75
+ # raise one of these two exceptions.
76
+ rescue Beanstalk::TimedOut
77
+ render json: { eat_job: "#{count} job(s) deleted" }
78
+ end
79
+
80
+ def move
81
+ from_tube = Base64.urlsafe_decode64(params[:id])
82
+ to_tube = Base64.urlsafe_decode64(params[:to_tube])
83
+
84
+ count = 0
85
+
86
+ pool.watch(from_tube)
87
+ pool.use(to_tube)
88
+ while true
89
+ job = pool.reserve(0)
90
+ if job.present?
91
+ count += 1
92
+ pool.put(job.body)
93
+ job.delete
94
+ else
95
+ break
96
+ end
97
+ end
98
+
99
+ # So, be aware that under ALL circumstances the loop will end with a
100
+ # Beanstalk::TimedOut exception. We simply catch it and report the
101
+ # number of jobs eaten. There is no case where this method will not
102
+ # raise one of these two exceptions.
103
+ rescue Beanstalk::TimedOut
104
+ render json: { eat_job: "#{count} job(s) moved from '#{from_tube}' to '#{to_tube}'" }
105
+ rescue Beanstalk::NotFoundError
106
+ render json: { eat_job: "Tube '#{tube}' not found" }
107
+ end
108
+
109
+ def worker
110
+ pool.use(tube)
111
+ pool.put({ type: :ayl, code: 'Kernel.exit' }.to_yaml)
112
+ render json: { kill_worker: "Put kill worker message on tube '#{tube}'" }
113
+ end
114
+
115
+ def kick
116
+ bound = params[:bound] || '1'
117
+
118
+ pool.use(tube)
119
+ connection.kick(bound.to_i)
120
+
121
+ render json: { kick: "Up to #{bound} jobs were kicked on tube '#{tube}'" }
122
+ end
123
+
124
+ private
125
+
126
+ def must_have_existing_tube
127
+ if params[:id]
128
+ @tube = Base64.urlsafe_decode64(params[:id])
129
+ begin
130
+ pool.stats_tube(@tube)
131
+ rescue Beanstalk::NotFoundError
132
+ render json: { error: "Specified tube does not exist: #{@tube}" }, status: 404
133
+ end
134
+ else
135
+ render json: { error: "Must specify tube name" }, status: 404
136
+ end
137
+ end
138
+
139
+ def must_have_tube
140
+ if params[:id]
141
+ @tube = Base64.urlsafe_decode64(params[:id])
142
+ else
143
+ render json: { error: "Must specify tube name" }, status: 404
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,4 @@
1
+ module BeanstalkApi
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>BeanstalkApi</title>
5
+ <%= stylesheet_link_tag "beanstalk_api/application", :media => "all" %>
6
+ <%= javascript_include_tag "beanstalk_api/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,19 @@
1
+ BeanstalkApi::Engine.routes.draw do
2
+
3
+ get '/' => 'tasks#overall_statistics'
4
+
5
+ resources :jobs, only: [ :show, :destroy ]
6
+
7
+ resources :tubes, only: [ :index, :show ] do
8
+ member do
9
+ get 'ready'
10
+ get 'delayed'
11
+ get 'buried'
12
+ delete 'one_job'
13
+ delete 'all_jobs'
14
+ delete 'move/:to_tube' => :move
15
+ delete 'worker'
16
+ get 'kick(/:bound)' => :kick
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ require "beanstalk_api/engine"
2
+
3
+ module BeanstalkApi
4
+ end
@@ -0,0 +1,11 @@
1
+ module BeanstalkApi
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace BeanstalkApi
4
+ config.generators do |g|
5
+ g.test_framework :rspec, :fixture => false
6
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
7
+ g.assets false
8
+ g.helper false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module BeanstalkApi
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :beanstalk_api do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe BeanstalkApi::ApplicationController do
4
+
5
+ context '#format_job' do
6
+
7
+ it "should deal with the specified job if it is a hash" do
8
+ mock_job1 = double("Job")
9
+ mock_job1.should_receive(:id).and_return(1)
10
+ mock_job1.should_receive(:body).and_return('hello world')
11
+
12
+ mock_job2 = double("Job")
13
+ mock_job2.should_receive(:id).and_return(2)
14
+ mock_job2.should_receive(:body).and_return('goodbye world')
15
+
16
+ job = { 'localhost' => mock_job1, 'otherhost' => mock_job2 }
17
+
18
+ expect(controller.send(:format_job, job)).to eq( { 1 => 'hello world', 2 => 'goodbye world' } )
19
+ end
20
+
21
+ it "should with the job if it is just a job" do
22
+ mock_job1 = double("Job")
23
+ mock_job1.should_receive(:id).and_return(1)
24
+ mock_job1.should_receive(:body).and_return('hello world')
25
+
26
+ expect(controller.send(:format_job, mock_job1)).to eq( { 1 => 'hello world' } )
27
+ end
28
+
29
+ end
30
+
31
+ context '#beanstalk_client' do
32
+
33
+ it "should retrive the active engine" do
34
+ mock_engine = double("BSEngine")
35
+
36
+ Ayl::Engine.should_receive(:get_active_engine).and_return(mock_engine)
37
+
38
+ expect(controller.send(:beanstalk_client)).to be(mock_engine)
39
+ end
40
+
41
+ end
42
+
43
+ context '#pool' do
44
+
45
+ it "should return the pool from the beanstalk client" do
46
+ mock_pool = double("BeanstalkPool")
47
+
48
+ mock_client = double("BeanstalkClient")
49
+ mock_client.should_receive(:pool).and_return(mock_pool)
50
+
51
+ controller.should_receive(:beanstalk_client).and_return(mock_client)
52
+
53
+ expect(controller.send(:pool)).to be(mock_pool)
54
+ end
55
+
56
+ end
57
+
58
+ context '#connection' do
59
+
60
+ it "should return the first open connection from the pool" do
61
+ mock_conn1 = double("BeanstalkConnection")
62
+ mock_conn2 = double("BeanstalkConnection")
63
+
64
+ mock_pool = double("BeanstalkPool")
65
+ mock_pool.should_receive(:open_connections).and_return([ mock_conn1, mock_conn2 ])
66
+
67
+ controller.should_receive(:pool).and_return(mock_pool)
68
+
69
+ expect(controller.send(:connection)).to be(mock_conn1)
70
+ end
71
+
72
+ end
73
+
74
+ context '#default_tube' do
75
+
76
+ it "should return 'default' if the ASYNC_TUBE constant is not defined" do
77
+ # expect(controller.send(:default_tube)).to eq('default')
78
+ end
79
+
80
+ it "should return the value of ASYNC_TUBE if ASYNC_TUBE is defined" do
81
+ ASYNC_TUBE ||= 'stupendous'
82
+ expect(controller.send(:default_tube)).to eq(ASYNC_TUBE)
83
+ end
84
+
85
+ end
86
+
87
+ end