documentcloud-cloud-crowd 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
-
1
+ =
2
2
  _ _
3
3
  ( ` )_
4
4
  ( ) `)
@@ -29,11 +29,20 @@
29
29
  * Built for Amazon EC2 and S3
30
30
  * split -> process -> merge
31
31
  * As easy as `gem install cloud-crowd`
32
+
33
+ Well-suited for:
34
+
35
+ * Generating or resizing images.
36
+ * Encoding video.
37
+ * Running text extraction or OCR on PDFs.
38
+ * Migrating a large file set or database.
39
+ * Web scraping.
32
40
 
33
41
 
34
- ~ Wiki ~
42
+ ~ Documentation ~
35
43
 
36
- http://wiki.github.com/documentcloud/cloud-crowd
44
+ Wiki: http://wiki.github.com/documentcloud/cloud-crowd
45
+ Rdoc: http://rdoc.info/projects/documentcloud/cloud-crowd
37
46
 
38
47
 
39
48
  ~ Getting started ~
data/cloud-crowd.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'cloud-crowd'
3
- s.version = '0.0.6' # Keep version in sync with cloud-cloud.rb
3
+ s.version = '0.1.0' # Keep version in sync with cloud-cloud.rb
4
4
  s.date = '2009-09-01'
5
5
 
6
6
  s.homepage = "http://wiki.github.com/documentcloud/cloud-crowd"
@@ -57,6 +57,8 @@ examples/word_count_example.rb
57
57
  lib/cloud-crowd.rb
58
58
  lib/cloud_crowd/action.rb
59
59
  lib/cloud_crowd/app.rb
60
+ lib/cloud_crowd/asset_store/filesystem_store.rb
61
+ lib/cloud_crowd/asset_store/s3_store.rb
60
62
  lib/cloud_crowd/asset_store.rb
61
63
  lib/cloud_crowd/command_line.rb
62
64
  lib/cloud_crowd/daemon.rb
@@ -87,10 +89,11 @@ public/images/sidebar_top.png
87
89
  public/images/worker_info.png
88
90
  public/images/worker_info_loading.gif
89
91
  public/js/admin_console.js
90
- public/js/excanvas.pack.js
91
- public/js/jquery.flot.pack.js
92
- public/js/jquery-1.3.2.min.js
92
+ public/js/excanvas.js
93
+ public/js/flot.js
94
+ public/js/jquery.js
93
95
  README
96
+ test/acceptance/test_app.rb
94
97
  test/acceptance/test_failing_work_units.rb
95
98
  test/acceptance/test_word_count.rb
96
99
  test/blueprints.rb
@@ -99,6 +102,8 @@ test/config/config.yml
99
102
  test/config/database.yml
100
103
  test/config/actions/failure_testing.rb
101
104
  test/test_helper.rb
105
+ test/unit/test_action.rb
106
+ test/unit/test_configuration.rb
102
107
  test/unit/test_job.rb
103
108
  test/unit/test_work_unit.rb
104
109
  views/index.erb
@@ -35,13 +35,13 @@
35
35
  # desired balance between latency and traffic.
36
36
 
37
37
  # The number of workers that `crowd workers start` spins up.
38
- :num_workers: 4
38
+ :num_workers: 3
39
39
 
40
40
  # The minimum number of seconds a worker waits between checking the job queue.
41
41
  :min_worker_wait: 1
42
42
 
43
43
  # The maximum number of seconds a worker waits between checking the job queue.
44
- :max_worker_wait: 20
44
+ :max_worker_wait: 5
45
45
 
46
46
  # The number of separate attempts that will be made to process an individual
47
47
  # work unit, before marking it as having failed.
data/lib/cloud-crowd.rb CHANGED
@@ -45,7 +45,7 @@ module CloudCrowd
45
45
  ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
46
46
 
47
47
  # Keep the version in sync with the gemspec.
48
- VERSION = '0.0.6'
48
+ VERSION = '0.1.0'
49
49
 
50
50
  # A Job is processing if its WorkUnits in the queue to be handled by workers.
51
51
  PROCESSING = 1
@@ -28,7 +28,6 @@ module CloudCrowd
28
28
  @job_id, @work_unit_id = options['job_id'], options['work_unit_id']
29
29
  @work_directory = File.expand_path(File.join(@store.temp_storage_path, storage_prefix))
30
30
  FileUtils.mkdir_p(@work_directory) unless File.exists?(@work_directory)
31
- Dir.chdir @work_directory
32
31
  status == MERGING ? parse_input : download_input
33
32
  end
34
33
 
@@ -53,13 +52,11 @@ module CloudCrowd
53
52
  def save(file_path)
54
53
  save_path = File.join(storage_prefix, File.basename(file_path))
55
54
  @store.save(file_path, save_path)
56
- return @store.url(save_path)
57
55
  end
58
56
 
59
57
  # After the Action has finished, we remove the work directory and return
60
58
  # to the root directory (where daemons run by default).
61
59
  def cleanup_work_directory
62
- Dir.chdir '/'
63
60
  FileUtils.rm_r(@work_directory) if File.exists?(@work_directory)
64
61
  end
65
62
 
@@ -68,8 +65,8 @@ module CloudCrowd
68
65
 
69
66
  # Convert an unsafe URL into a filesystem-friendly filename.
70
67
  def safe_filename(url)
71
- ext = File.extname(url)
72
- name = File.basename(url).gsub(/%\d+/, '-').gsub(/[^a-zA-Z0-9_\-.]/, '')
68
+ ext = File.extname(url)
69
+ name = URI.unescape(File.basename(url)).gsub(/[^a-zA-Z0-9_\-.]/, '-').gsub(/-+/, '-')
73
70
  File.basename(name, ext).gsub('.', '-') + ext
74
71
  end
75
72
 
@@ -90,11 +87,13 @@ module CloudCrowd
90
87
 
91
88
  # If the input is a URL, download the file before beginning processing.
92
89
  def download_input
93
- input_is_url = !!URI.parse(@input) rescue false
94
- return unless input_is_url
95
- @input_path = File.join(@work_directory, safe_filename(@input))
96
- @file_name = File.basename(@input_path, File.extname(@input_path))
97
- download(@input, @input_path)
90
+ Dir.chdir(@work_directory) do
91
+ input_is_url = !!URI.parse(@input) rescue false
92
+ return unless input_is_url
93
+ @input_path = File.join(@work_directory, safe_filename(@input))
94
+ @file_name = File.basename(@input_path, File.extname(@input_path))
95
+ download(@input, @input_path)
96
+ end
98
97
  end
99
98
 
100
99
  end
@@ -75,7 +75,7 @@ module CloudCrowd
75
75
  # Cleans up a Job's saved S3 files. Delete a Job after you're done
76
76
  # downloading the results.
77
77
  delete '/jobs/:job_id' do
78
- current_job.cleanup
78
+ current_job.destroy
79
79
  json nil
80
80
  end
81
81
 
@@ -10,19 +10,24 @@ module CloudCrowd
10
10
  # and +save+ methods use it behind the scenes.
11
11
  class AssetStore
12
12
 
13
+ autoload :S3Store, 'cloud_crowd/asset_store/s3_store'
14
+ autoload :FilesystemStore, 'cloud_crowd/asset_store/filesystem_store'
15
+
13
16
  LOCAL_STORAGE_PATH = '/tmp/cloud_crowd_storage'
14
17
 
15
- # Creating an AssetStore mixes in the specific storage implementation
18
+ # Configure the AssetStore with the specific storage implementation
16
19
  # specified by 'storage' in <tt>config.yml</tt>.
20
+ case CloudCrowd.config[:storage]
21
+ when 's3' then include S3Store
22
+ when 'filesystem' then include FilesystemStore
23
+ else raise Error::StorageNotFound, "#{CloudCrowd.config[:storage]} is not a valid storage back end"
24
+ end
25
+
26
+ # Creating the AssetStore ensures that its scratch directory exists.
17
27
  def initialize
18
28
  @use_auth = CloudCrowd.config[:use_s3_authentication]
19
- @storage = CloudCrowd.config[:storage]
20
29
  FileUtils.mkdir_p temp_storage_path unless File.exists? temp_storage_path
21
- case @storage
22
- when 's3' then extend S3Store
23
- when 'filesystem' then extend FilesystemStore
24
- else raise StorageNotFound, "#{@storage} is not a valid storage back end"
25
- end
30
+ raise Error::StorageNotWritable, "#{temp_storage_path} is not writable" unless File.writable?(temp_storage_path)
26
31
  end
27
32
 
28
33
  # Get the path to CloudCrowd's temporary local storage. All actions run
@@ -30,71 +35,6 @@ module CloudCrowd
30
35
  def temp_storage_path
31
36
  "#{Dir.tmpdir}/cloud_crowd_tmp"
32
37
  end
33
-
34
-
35
- # The S3Store is an implementation of an AssetStore that uses a bucket
36
- # on S3 for all resulting files.
37
- module S3Store
38
-
39
- # Save a finished file from local storage to S3. Save it publicly unless
40
- # we're configured to use S3 authentication.
41
- def save(local_path, save_path)
42
- ensure_s3_connection
43
- permission = @use_auth ? 'private' : 'public-read'
44
- @bucket.put(save_path, File.open(local_path), {}, permission)
45
- end
46
-
47
- # Return the S3 public URL for a finshed file. Authenticated links expire
48
- # after one day by default.
49
- def url(save_path)
50
- @use_auth ? @s3.interface.get_link(@bucket, save_path) :
51
- @bucket.key(save_path).public_link
52
- end
53
-
54
- # Remove all of a Job's resulting files from S3, both intermediate and finished.
55
- def cleanup_job(job)
56
- ensure_s3_connection
57
- @bucket.delete_folder("#{job.action}/job_#{job.id}")
58
- end
59
-
60
- # Workers, through the course of many WorkUnits, keep around an AssetStore.
61
- # Ensure we have a persistent S3 connection after first use.
62
- def ensure_s3_connection
63
- unless @s3 && @bucket
64
- params = {:port => 80, :protocol => 'http'}
65
- @s3 = RightAws::S3.new(CloudCrowd.config[:aws_access_key], CloudCrowd.config[:aws_secret_key], params)
66
- @bucket = @s3.bucket(CloudCrowd.config[:s3_bucket], true)
67
- end
68
- end
69
- end
70
-
71
-
72
- # The FilesystemStore is an implementation of the AssetStore, good only for
73
- # use in development, testing, or if you're only running a single-machine
74
- # installation.
75
- module FilesystemStore
76
-
77
- # Save a file to somewhere semi-persistent on the filesystem. Can be used
78
- # in development, when offline, or if you happen to have a single-machine
79
- # CloudCrowd installation. To use, configure :local_storage.
80
- def save(local_path, save_path)
81
- save_path = File.join(LOCAL_STORAGE_PATH, save_path)
82
- save_dir = File.dirname(save_path)
83
- FileUtils.mkdir_p save_dir unless File.exists? save_dir
84
- FileUtils.cp(local_path, save_path)
85
- end
86
-
87
- # Return the URL for a file saved to the local filesystem.
88
- def url(save_path)
89
- "file://#{File.expand_path(File.join(LOCAL_STORAGE_PATH, save_path))}"
90
- end
91
-
92
- # Remove all of a Job's result files from the filesystem.
93
- def cleanup_job(job)
94
- path = "#{LOCAL_STORAGE_PATH}/#{job.action}/job_#{job.id}"
95
- FileUtils.rm_r(path) if File.exists?(path)
96
- end
97
- end
98
38
 
99
39
  end
100
40
 
@@ -0,0 +1,28 @@
1
+ module CloudCrowd
2
+ class AssetStore
3
+
4
+ # The FilesystemStore is an implementation of the AssetStore, good only for
5
+ # use in development, testing, or if you're only running a single-machine
6
+ # installation.
7
+ module FilesystemStore
8
+
9
+ # Save a file to somewhere semi-persistent on the filesystem. Can be used
10
+ # in development, when offline, or if you happen to have a single-machine
11
+ # CloudCrowd installation. To use, configure <tt>:storage => 'filesystem'</tt>.
12
+ def save(local_path, save_path)
13
+ save_path = File.join(LOCAL_STORAGE_PATH, save_path)
14
+ save_dir = File.dirname(save_path)
15
+ FileUtils.mkdir_p save_dir unless File.exists? save_dir
16
+ FileUtils.cp(local_path, save_path)
17
+ "file://#{File.expand_path(save_path)}"
18
+ end
19
+
20
+ # Remove all of a Job's result files from the filesystem.
21
+ def cleanup(job)
22
+ path = "#{LOCAL_STORAGE_PATH}/#{job.action}/job_#{job.id}"
23
+ FileUtils.rm_r(path) if File.exists?(path)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ module CloudCrowd
2
+ class AssetStore
3
+
4
+ # The S3Store is an implementation of an AssetStore that uses a bucket
5
+ # on S3 for all resulting files.
6
+ module S3Store
7
+
8
+ # Save a finished file from local storage to S3. Save it publicly unless
9
+ # we're configured to use S3 authentication. Authenticated links expire
10
+ # after one day by default.
11
+ def save(local_path, save_path)
12
+ ensure_s3_connection
13
+ if @use_auth
14
+ @bucket.put(save_path, File.open(local_path), {}, 'private')
15
+ @s3.interface.get_link(@bucket, save_path)
16
+ else
17
+ @bucket.put(save_path, File.open(local_path), {}, 'public-read')
18
+ @bucket.key(save_path).public_link
19
+ end
20
+ end
21
+
22
+ # Remove all of a Job's resulting files from S3, both intermediate and finished.
23
+ def cleanup(job)
24
+ ensure_s3_connection
25
+ @bucket.delete_folder("#{job.action}/job_#{job.id}")
26
+ end
27
+
28
+ # Workers, through the course of many WorkUnits, keep around an AssetStore.
29
+ # Ensure we have a persistent S3 connection after first use.
30
+ def ensure_s3_connection
31
+ unless @s3 && @bucket
32
+ params = {:port => 80, :protocol => 'http'}
33
+ @s3 = RightAws::S3.new(CloudCrowd.config[:aws_access_key], CloudCrowd.config[:aws_secret_key], params)
34
+ @bucket = @s3.bucket(CloudCrowd.config[:s3_bucket], true)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -17,6 +17,7 @@ module CloudCrowd
17
17
  CloudCrowd is a MapReduce-inspired Parallel Processing System for Ruby.
18
18
 
19
19
  Wiki: http://wiki.github.com/documentcloud/cloud-crowd
20
+ Rdoc: http://rdoc.info/projects/documentcloud/cloud-crowd
20
21
 
21
22
  Usage: crowd COMMAND OPTIONS
22
23
 
@@ -1,22 +1,28 @@
1
1
  module CloudCrowd
2
2
 
3
3
  # Base Error class which all custom CloudCrowd exceptions inherit from.
4
- class Error < RuntimeError #:nodoc:
5
- end
6
-
7
- # ActionNotFound is raised when a job is created for an action that doesn't
8
- # exist.
9
- class ActionNotFound < Error #:nodoc:
10
- end
11
-
12
- # StorageNotFound is raised when config.yml specifies a storage back end that
13
- # doesn't exist.
14
- class StorageNotFound < Error #:nodoc:
15
- end
4
+ # Rescuing CloudCrowd::Error (or RuntimeError) will get all custom exceptions.
5
+ class Error < RuntimeError
6
+
7
+ # ActionNotFound is raised when a job is created for an action that doesn't
8
+ # exist.
9
+ class ActionNotFound < Error
10
+ end
16
11
 
17
- # StatusUnspecified is raised when a WorkUnit returns without a valid
18
- # status code.
19
- class StatusUnspecified < Error #:nodoc:
12
+ # StorageNotFound is raised when config.yml specifies a storage back end that
13
+ # doesn't exist.
14
+ class StorageNotFound < Error
15
+ end
16
+
17
+ # If the AssetStore can't write to its scratch directory.
18
+ class StorageNotWritable < Error
19
+ end
20
+
21
+ # StatusUnspecified is raised when a WorkUnit returns without a valid
22
+ # status code.
23
+ class StatusUnspecified < Error
24
+ end
25
+
20
26
  end
21
27
 
22
28
  end
@@ -2,7 +2,7 @@ require 'cloud_crowd/helpers/authorization'
2
2
  require 'cloud_crowd/helpers/resources'
3
3
 
4
4
  module CloudCrowd
5
- module Helpers #:nodoc:
5
+ module Helpers
6
6
  include Authorization, Resources #, Rack::Utils
7
7
  end
8
8
  end
@@ -1,7 +1,7 @@
1
1
  module CloudCrowd
2
2
 
3
3
  # Pilfered in parts from the ActiveSupport::Inflector.
4
- module Inflector #:nodoc:
4
+ module Inflector
5
5
 
6
6
  def self.camelize(word)
7
7
  word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
@@ -13,7 +13,7 @@ module CloudCrowd
13
13
 
14
14
  before_validation_on_create :set_initial_status
15
15
  after_create :queue_for_workers
16
- before_destroy :cleanup
16
+ before_destroy :cleanup_assets
17
17
 
18
18
  # Create a Job from an incoming JSON or XML request, and add it to the queue.
19
19
  # TODO: Think about XML support.
@@ -61,9 +61,9 @@ module CloudCrowd
61
61
  end
62
62
 
63
63
  # Cleaning up after a job will remove all of its files from S3. Destroying
64
- # a Job calls cleanup first.
65
- def cleanup
66
- AssetStore.new.cleanup_job(self)
64
+ # a Job calls cleanup_assets first.
65
+ def cleanup_assets
66
+ AssetStore.new.cleanup(self)
67
67
  end
68
68
 
69
69
  # Have all of the WorkUnits finished?
@@ -94,7 +94,7 @@ module CloudCrowd
94
94
  def action_class
95
95
  klass = CloudCrowd.actions[self.action]
96
96
  return klass if klass
97
- raise ActionNotFound, "no action named: '#{self.action}' could be found"
97
+ raise Error::ActionNotFound, "no action named: '#{self.action}' could be found"
98
98
  end
99
99
 
100
100
  # How complete is this Job?
@@ -113,12 +113,15 @@ module CloudCrowd
113
113
  # Executes the current work unit, catching all exceptions as failures.
114
114
  def run_work_unit
115
115
  begin
116
+ result = nil
116
117
  @action = CloudCrowd.actions[@action_name].new(@status, @input, @options, @store)
117
- result = case @status
118
- when PROCESSING then @action.process
119
- when SPLITTING then @action.split
120
- when MERGING then @action.merge
121
- else raise StatusUnspecified, "work units must specify their status"
118
+ Dir.chdir(@action.work_directory) do
119
+ result = case @status
120
+ when PROCESSING then @action.process
121
+ when SPLITTING then @action.split
122
+ when MERGING then @action.merge
123
+ else raise Error::StatusUnspecified, "work units must specify their status"
124
+ end
122
125
  end
123
126
  complete_work_unit({'output' => result}.to_json)
124
127
  rescue Exception => e
File without changes
File without changes
File without changes
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ class AppTest < Test::Unit::TestCase
4
+
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ CloudCrowd::App
9
+ end
10
+
11
+ context "The CloudCrowd::App (Sinatra)" do
12
+
13
+ setup do
14
+ CloudCrowd::Job.destroy_all
15
+ 2.times { CloudCrowd::Job.make }
16
+ end
17
+
18
+ should "be able to render the Operations Center (GET /)" do
19
+ get '/'
20
+ assert last_response.body.include? '<div id="workers">'
21
+ assert last_response.body.include? '<div id="graphs">'
22
+ end
23
+
24
+ should "be able to get the current status for all jobs (GET /status)" do
25
+ resp = JSON.parse(get('/status').body)
26
+ assert resp['jobs'].length == 2
27
+ assert resp['jobs'][0]['status'] == 'processing'
28
+ assert resp['jobs'][0]['percent_complete'] == 0
29
+ assert resp['work_unit_count'] == 2
30
+ end
31
+
32
+ should "be able to check in a worker daemon, and then check out a work unit" do
33
+ put '/worker', :name => '101@localhost', :thread_status => 'sleeping'
34
+ assert last_response.successful? && last_response.empty?
35
+ post '/work', :worker_name => '101@localhost', :worker_actions => 'graphics_magick'
36
+ checked_out = JSON.parse(last_response.body)
37
+ assert checked_out['action'] == 'graphics_magick'
38
+ assert checked_out['attempts'] == 0
39
+ assert checked_out['status'] == CloudCrowd::PROCESSING
40
+ status_check = JSON.parse(get('/worker/101@localhost').body)
41
+ assert checked_out == status_check
42
+ end
43
+
44
+ should "have a heartbeat" do
45
+ assert get('/heartbeat').body == 'buh-bump'
46
+ end
47
+
48
+ should "be able to create a job" do
49
+ post('/jobs', :job => '{"action":"graphics_magick","inputs":["http://www.google.com/"]}')
50
+ assert last_response.ok?
51
+ job_info = JSON.parse(last_response.body)
52
+ assert job_info['percent_complete'] == 0
53
+ assert job_info['work_units'] == 1
54
+ assert CloudCrowd::Job.last.id == job_info['id']
55
+ end
56
+
57
+ should "be able to check in on the status of a job" do
58
+ get("/jobs/#{CloudCrowd::Job.last.id}")
59
+ assert last_response.ok?
60
+ assert JSON.parse(last_response.body)['percent_complete'] == 0
61
+ end
62
+
63
+ should "be able to clean up a job when we're done with it" do
64
+ id = CloudCrowd::Job.last.id
65
+ delete("/jobs/#{id}")
66
+ assert last_response.successful? && last_response.empty?
67
+ assert !CloudCrowd::Job.find_by_id(id)
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class CloudCrowd::Action
4
+ public :safe_filename
5
+ end
6
+
7
+ class EmptyAction < CloudCrowd::Action
8
+ end
9
+
10
+ class ActionTest < Test::Unit::TestCase
11
+
12
+ context "A CloudCrowd Job" do
13
+
14
+ setup do
15
+ @store = CloudCrowd::AssetStore.new
16
+ @args = [CloudCrowd::PROCESSING, 'file://' + File.expand_path(__FILE__), {'job_id' => 1, 'work_unit_id' => 1}, @store]
17
+ @action = CloudCrowd.actions['word_count'].new(*@args)
18
+ end
19
+
20
+ should "throw an exception if the 'process' method isn't implemented" do
21
+ assert_raise(NotImplementedError) { EmptyAction.new(*@args).process }
22
+ end
23
+
24
+ should "have downloaded the input URL to local storage" do
25
+ assert @action.input_path
26
+ assert File.read(@action.input_path) == File.read(File.expand_path(__FILE__))
27
+ end
28
+
29
+ should "be able to save (to the filesystem while testing)" do
30
+ assert @action.save(@action.input_path) == "file://#{CloudCrowd::AssetStore::LOCAL_STORAGE_PATH}/word_count/job_1/unit_1/test_action.rb"
31
+ end
32
+
33
+ should "be able to clean up after itself" do
34
+ @action.cleanup_work_directory
35
+ assert !File.exists?(@action.work_directory)
36
+ end
37
+
38
+ should "be able to generate a safe filename for a URL to write to disk" do
39
+ name = @action.safe_filename("http://example.com/Some%20(Crazy'Kinda%7E)'Filename.txt")
40
+ assert name == 'Some-Crazy-Kinda-Filename.txt'
41
+ end
42
+
43
+ should "be able to count the number of words in this file" do
44
+ assert @action.process == 149
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigurationTest < Test::Unit::TestCase
4
+
5
+ context "CloudCrowd Configuration" do
6
+
7
+ should "have read in config.yml" do
8
+ assert CloudCrowd.config[:num_workers] == 4
9
+ assert CloudCrowd.config[:storage] == 'filesystem'
10
+ end
11
+
12
+ should "allow config.yml to configure the implementation of AssetStore" do
13
+ assert CloudCrowd::AssetStore.ancestors.include?(CloudCrowd::AssetStore::FilesystemStore)
14
+ end
15
+
16
+ should "have properly configured the ActiveRecord database" do
17
+ assert ActiveRecord::Base.connection.active?
18
+ end
19
+
20
+ should "have loaded in the default set of actions" do
21
+ assert CloudCrowd.actions['word_count'] == WordCount
22
+ assert CloudCrowd.actions['process_pdfs'] == ProcessPdfs
23
+ assert CloudCrowd.actions['graphics_magick'] == GraphicsMagick
24
+ end
25
+
26
+ end
27
+
28
+ end
data/views/index.erb CHANGED
@@ -5,9 +5,9 @@
5
5
  <title>Operations Center | CloudCrowd</title>
6
6
  <link href="/css/reset.css" media="screen" rel="stylesheet" type="text/css" />
7
7
  <link href="/css/admin_console.css" media="screen" rel="stylesheet" type="text/css" />
8
- <script src="/js/jquery-1.3.2.min.js" type="text/javascript"></script>
9
- <!--[if IE]><script src="/js/excanvas.pack.js" type="text/javascript"></script><![endif]-->
10
- <script src="/js/jquery.flot.pack.js" type="text/javascript"></script>
8
+ <script src="/js/jquery.js" type="text/javascript"></script>
9
+ <!--[if IE]><script src="/js/excanvas.js" type="text/javascript"></script><![endif]-->
10
+ <script src="/js/flot.js" type="text/javascript"></script>
11
11
  <script src="/js/admin_console.js" type="text/javascript"></script>
12
12
  </head>
13
13
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: documentcloud-cloud-crowd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Ashkenas
@@ -145,6 +145,8 @@ files:
145
145
  - lib/cloud-crowd.rb
146
146
  - lib/cloud_crowd/action.rb
147
147
  - lib/cloud_crowd/app.rb
148
+ - lib/cloud_crowd/asset_store/filesystem_store.rb
149
+ - lib/cloud_crowd/asset_store/s3_store.rb
148
150
  - lib/cloud_crowd/asset_store.rb
149
151
  - lib/cloud_crowd/command_line.rb
150
152
  - lib/cloud_crowd/daemon.rb
@@ -175,10 +177,11 @@ files:
175
177
  - public/images/worker_info.png
176
178
  - public/images/worker_info_loading.gif
177
179
  - public/js/admin_console.js
178
- - public/js/excanvas.pack.js
179
- - public/js/jquery.flot.pack.js
180
- - public/js/jquery-1.3.2.min.js
180
+ - public/js/excanvas.js
181
+ - public/js/flot.js
182
+ - public/js/jquery.js
181
183
  - README
184
+ - test/acceptance/test_app.rb
182
185
  - test/acceptance/test_failing_work_units.rb
183
186
  - test/acceptance/test_word_count.rb
184
187
  - test/blueprints.rb
@@ -187,6 +190,8 @@ files:
187
190
  - test/config/database.yml
188
191
  - test/config/actions/failure_testing.rb
189
192
  - test/test_helper.rb
193
+ - test/unit/test_action.rb
194
+ - test/unit/test_configuration.rb
190
195
  - test/unit/test_job.rb
191
196
  - test/unit/test_work_unit.rb
192
197
  - views/index.erb