acts_as_executor 1.0.0.beta2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/acts_as_executor.gemspec +8 -1
  2. data/lib/acts_as_executor.rb +18 -31
  3. data/lib/acts_as_executor/common/future_task.rb +2 -2
  4. data/lib/acts_as_executor/common/instance_support_methods.rb +15 -0
  5. data/lib/acts_as_executor/executor/factory.rb +18 -22
  6. data/lib/acts_as_executor/executor/model/class_methods.rb +33 -0
  7. data/lib/acts_as_executor/executor/model/instance_methods.rb +87 -0
  8. data/lib/acts_as_executor/executor/model/instance_support_methods.rb +36 -0
  9. data/lib/acts_as_executor/task/clazz.rb +26 -0
  10. data/lib/acts_as_executor/task/model/class_methods.rb +13 -0
  11. data/lib/acts_as_executor/task/model/instance_methods.rb +64 -0
  12. data/lib/acts_as_executor/task/model/instance_support_methods.rb +32 -0
  13. data/lib/acts_as_executor/validators/class_exists_validator.rb +1 -1
  14. data/lib/acts_as_executor/validators/class_includes_validator.rb +10 -0
  15. data/lib/acts_as_executor/version.rb +1 -1
  16. data/lib/generators/{executor_generator.rb → acts_as_executor_generator.rb} +1 -5
  17. data/lib/generators/templates/migration/create_executors.rb +2 -2
  18. data/spec/acts_as_executor_spec.rb +22 -0
  19. data/spec/common/future_task_spec.rb +21 -0
  20. data/spec/common/instance_support_methods_spec.rb +25 -0
  21. data/spec/common/units_spec.rb +19 -0
  22. data/spec/executor/factory_spec.rb +31 -0
  23. data/spec/executor/model/class_methods_spec.rb +49 -0
  24. data/spec/executor/model/instance_methods_spec.rb +150 -0
  25. data/spec/executor/model/instance_support_methods_spec.rb +70 -0
  26. data/spec/spec_helper.rb +8 -0
  27. data/spec/support/blueprints.rb +20 -0
  28. data/spec/support/classes.rb +55 -0
  29. data/spec/support/custom_matchers.rb +26 -0
  30. data/spec/support/database_connection.rb +4 -0
  31. data/spec/task/clazz_spec.rb +42 -0
  32. data/spec/task/model/class_methods_spec.rb +13 -0
  33. data/spec/task/model/instance_methods_spec.rb +80 -0
  34. data/spec/task/model/instance_support_methods_spec.rb +56 -0
  35. data/spec/task/schedules_spec.rb +11 -0
  36. data/spec/validators/class_exists_validator_spec.rb +21 -0
  37. data/spec/validators/class_includes_validator_spec.rb +21 -0
  38. metadata +88 -21
  39. data/README.rdoc +0 -67
  40. data/lib/acts_as_executor/executor/kinds.rb +0 -16
  41. data/lib/acts_as_executor/executor/model/actions.rb +0 -65
  42. data/lib/acts_as_executor/executor/model/associations.rb +0 -11
  43. data/lib/acts_as_executor/executor/model/attributes.rb +0 -16
  44. data/lib/acts_as_executor/executor/model/base.rb +0 -24
  45. data/lib/acts_as_executor/executor/model/helpers.rb +0 -25
  46. data/lib/acts_as_executor/executor/model/validations.rb +0 -13
  47. data/lib/acts_as_executor/task/model/actions.rb +0 -34
  48. data/lib/acts_as_executor/task/model/associations.rb +0 -11
  49. data/lib/acts_as_executor/task/model/attributes.rb +0 -16
  50. data/lib/acts_as_executor/task/model/base.rb +0 -15
  51. data/lib/acts_as_executor/task/model/helpers.rb +0 -14
  52. data/lib/acts_as_executor/task/model/validations.rb +0 -16
  53. data/lib/generators/templates/initializer/acts_as_executor.rb +0 -4
@@ -1,7 +1,7 @@
1
1
  class ClassExistsValidator < ActiveModel::EachValidator
2
2
  def validate_each record, attribute, value
3
3
  begin
4
- value.constantize.new
4
+ Object.const_get(value).new
5
5
  rescue NameError
6
6
  record.errors[attribute] << "does not exist as a Class"
7
7
  end
@@ -0,0 +1,10 @@
1
+ class ClassIncludesValidator < ActiveModel::EachValidator
2
+ def validate_each record, attribute, value
3
+ begin
4
+ throw TypeError unless Object.const_get(value).new.kind_of? options[:includes]
5
+ rescue NameError
6
+ rescue TypeError
7
+ record.errors[attribute] << "does not include " + options[:includes].to_s
8
+ end
9
+ end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module ActsAsExecutor
2
- VERSION = "1.0.0.beta2"
2
+ VERSION = "1.0.0.rc1"
3
3
  end
@@ -1,12 +1,8 @@
1
- class ExecutorGenerator < Rails::Generators::NamedBase
1
+ class ActsAsExecutorGenerator < Rails::Generators::NamedBase
2
2
  include Rails::Generators::Migration
3
3
 
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
 
6
- def initializer
7
- template File.join("initializer", "acts_as_executor.rb"), File.join("config", "initializers", "acts_as_executor.rb")
8
- end
9
-
10
6
  def models
11
7
  template File.join("models", "executor.rb"), File.join("app", "models", file_name + ".rb")
12
8
  template File.join("models", "executor_task.rb"), File.join("app", "models", file_name + "_task.rb")
@@ -2,8 +2,8 @@ class Create<%= class_name.pluralize %> < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :<%= table_name %> do |t|
4
4
  t.string :name
5
- t.string :kind
6
- t.integer :size
5
+ t.integer :max_tasks
6
+ t.boolean :schedulable
7
7
  t.timestamps
8
8
  end
9
9
 
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor do
4
+ it { ActsAsExecutor::VERSION.should == "1.0.0.rc1" }
5
+
6
+ describe "#rails_initialized?" do
7
+ context "when rails initialized" do
8
+ it "should return true" do
9
+ $0 = "rails"
10
+
11
+ ActsAsExecutor.rails_initialized?.should be_true
12
+ end
13
+ end
14
+ context "when rails not initialized" do
15
+ it "should return false" do
16
+ $0 = "rake"
17
+
18
+ ActsAsExecutor.rails_initialized?.should be_false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Common::FutureTask do
4
+ before(:each) { @model = ActsAsExecutor::Common::FutureTask.new Clazz.make, nil }
5
+
6
+ it { @model.should be_a Java::java.util.concurrent.FutureTask }
7
+
8
+ describe "#done" do
9
+ context "when done handler exists" do
10
+ it "should invoke handler" do
11
+ handler = double "Handler"
12
+ handler.stub :done_handler
13
+ @model.done_handler = handler.method :done_handler
14
+
15
+ handler.should_receive :done_handler
16
+
17
+ @model.done
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Common::InstanceSupportMethods do
4
+ before(:each) { @model = InstanceSupportMethods.make }
5
+
6
+ it { @model.should_not allow_public_access_for_methods :log_statement, :log_message }
7
+
8
+ describe "#log_statement" do
9
+ it "should return a string" do
10
+ @model.send(:log_statement, "name", "statement").should == log_statement("name", "statement")
11
+ end
12
+ end
13
+
14
+ describe "#log_message" do
15
+ it "should return a string" do
16
+ @model.send(:log_message, "name", "doing", 1.to_s, "clazz", "message").should == log_message("name", "doing", 1.to_s, "clazz", "message")
17
+ end
18
+
19
+ context "when message is a hash" do
20
+ it "should return a string" do
21
+ @model.send(:log_message, "name", "doing", 1.to_s, "clazz", {:attribute_one => "attribute_one_value", :attribute_two => "attribute_two_value"}).should == log_message("name", "doing", 1.to_s, "clazz", {:attribute_one => "attribute_one_value", :attribute_two => "attribute_two_value"})
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Common::Units do
4
+ it { ActsAsExecutor::Common::Units::NANOSECONDS.should == "nanoseconds" }
5
+ it { ActsAsExecutor::Common::Units::MICROSECONDS.should == "microseconds" }
6
+ it { ActsAsExecutor::Common::Units::MILLISECONDS.should == "milliseconds" }
7
+ it { ActsAsExecutor::Common::Units::SECONDS.should == "seconds" }
8
+ it { ActsAsExecutor::Common::Units::MINUTES.should == "minutes" }
9
+ it { ActsAsExecutor::Common::Units::HOURS.should == "hours" }
10
+ it { ActsAsExecutor::Common::Units::DAYS.should == "days" }
11
+ it { ActsAsExecutor::Common::Units::ALL.should include ActsAsExecutor::Common::Units::NANOSECONDS,
12
+ ActsAsExecutor::Common::Units::MICROSECONDS,
13
+ ActsAsExecutor::Common::Units::MILLISECONDS,
14
+ ActsAsExecutor::Common::Units::SECONDS,
15
+ ActsAsExecutor::Common::Units::MINUTES,
16
+ ActsAsExecutor::Common::Units::HOURS,
17
+ ActsAsExecutor::Common::Units::DAYS
18
+ }
19
+ end
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Executor::Factory do
4
+ describe "#create" do
5
+ it "should return a scheduled executor" do
6
+ executor = ActsAsExecutor::Executor::Factory.create 5, true
7
+
8
+ executor.should be_a Java::java.util.concurrent.ScheduledExecutorService
9
+ executor.core_pool_size.should == 5
10
+ end
11
+
12
+ it "should return a single scheduled executor" do
13
+ ActsAsExecutor::Executor::Factory.create(1, true).should be_a Java::java.util.concurrent.ScheduledExecutorService
14
+ end
15
+
16
+ it "should return a cached executor" do
17
+ ActsAsExecutor::Executor::Factory.create(nil, nil).should be_a Java::java.util.concurrent.ExecutorService
18
+ end
19
+
20
+ it "should return a fixed executor" do
21
+ executor = ActsAsExecutor::Executor::Factory.create 5, nil
22
+
23
+ executor.should be_a Java::java.util.concurrent.ExecutorService
24
+ executor.core_pool_size.should == 5
25
+ end
26
+
27
+ it "should return a single executor" do
28
+ ActsAsExecutor::Executor::Factory.create(1, nil).should be_a Java::java.util.concurrent.ExecutorService
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Executor::Model::ClassMethods do
4
+ subject { ActiveRecord::Base }
5
+ it { should be_a ActsAsExecutor::Executor::Model::ClassMethods }
6
+
7
+ describe "#acts_as_executor" do
8
+ subject { Executor }
9
+ it { should include ActsAsExecutor::Common::InstanceSupportMethods }
10
+ it { should include ActsAsExecutor::Executor::Model::InstanceMethods }
11
+ it { should include ActsAsExecutor::Executor::Model::InstanceSupportMethods }
12
+
13
+ context "when arguments exist" do
14
+ before :all do
15
+ @log = Logger.new
16
+
17
+ ExecutorWithoutActsAsExecutor.acts_as_executor :log => @log
18
+ end
19
+
20
+ it "should set log" do
21
+ Rails.should_not_receive(:logger)
22
+ ExecutorWithoutActsAsExecutor.log.should == @log
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "#log" do
28
+ after(:each) { Executor.log = Logger.new }
29
+
30
+ it "should return log" do
31
+ log = Logger.new
32
+ Executor.log = log
33
+
34
+ Rails.should_not_receive(:logger)
35
+ Executor.log.should == log
36
+ end
37
+
38
+ context "when log has not been set" do
39
+ it "should return Rails.logger" do
40
+ log = Logger.new
41
+
42
+ Executor.log = nil
43
+
44
+ Rails.should_receive(:logger).at_most(:twice).and_return log
45
+ Executor.log.should == Rails.logger
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,150 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Executor::Model::InstanceMethods do
4
+ before(:each) { @model = Executor.make }
5
+
6
+ it { @model.should_not allow_public_access_for_methods :startup, :execute, :shutdown, :forced_shutdown }
7
+
8
+ describe "#startup" do
9
+ it "should create executor" do
10
+ @model.send(:executor).should be_nil
11
+
12
+ @model.send(:log).should_receive(:debug).with log_statement(@model.name, "startup triggered")
13
+ @model.send(:log).should_receive(:info).with log_statement(@model.name, "started")
14
+
15
+ @model.send :startup
16
+
17
+ @model.send(:executor).should_not be_nil
18
+ end
19
+ end
20
+
21
+ describe "#execute" do
22
+ before(:each) { @clazz = Clazz.make }
23
+ after(:each) { @model.send(:executor).shutdown }
24
+
25
+ it "should execute a task" do
26
+ @model.send :startup
27
+
28
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (one time)")
29
+ @model.send(:log).should_receive(:info).with log_message(@model.name, "enqueued", 1.to_s, @clazz.class.name)
30
+
31
+ future = @model.send :execute, @clazz, 1.to_s
32
+ future.get
33
+
34
+ future.is_done.should be_true
35
+ end
36
+
37
+ it "should execute a one shot task" do
38
+ @model = Executor.make :max_tasks => 1, :schedulable => true
39
+ @model.send :startup
40
+
41
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (one shot)")
42
+ @model.send(:log).should_receive(:info).with log_message(@model.name, "enqueued", 1.to_s, @clazz.class.name)
43
+
44
+ future = @model.send :execute, @clazz, 1.to_s, ActsAsExecutor::Task::Schedules::ONE_SHOT, 0, nil, ActsAsExecutor::Common::Units::SECONDS
45
+ future.get
46
+
47
+ future.is_done.should be_true
48
+ end
49
+
50
+ it "should execute a fixed delay task" do
51
+ @model = Executor.make :max_tasks => 1, :schedulable => true
52
+ @model.send :startup
53
+
54
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (fixed delay)")
55
+ @model.send(:log).should_receive(:info).with log_message(@model.name, "enqueued", 1.to_s, @clazz.class.name)
56
+
57
+ future = @model.send :execute, @clazz, 1.to_s, ActsAsExecutor::Task::Schedules::FIXED_DELAY, 0, 2, ActsAsExecutor::Common::Units::SECONDS
58
+
59
+ future.should_not be_nil
60
+ end
61
+
62
+ it "should execute a fixed rate task" do
63
+ @model = Executor.make :max_tasks => 1, :schedulable => true
64
+ @model.send :startup
65
+
66
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (fixed rate)")
67
+ @model.send(:log).should_receive(:info).with log_message(@model.name, "enqueued", 1.to_s, @clazz.class.name)
68
+
69
+ future = @model.send :execute, @clazz, 1.to_s, ActsAsExecutor::Task::Schedules::FIXED_RATE, 0, 2, ActsAsExecutor::Common::Units::SECONDS
70
+
71
+ future.should_not be_nil
72
+ end
73
+
74
+ context "when rejected execution exception is thrown" do
75
+ it "should log exception" do
76
+ @model.send :startup
77
+
78
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (one time)")
79
+ @model.send(:executor).should_receive(:execute).and_raise Java::java.util.concurrent.RejectedExecutionException.new
80
+ @model.send(:log).should_receive(:warn).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "encountered a rejected execution exception")
81
+
82
+ @model.send :execute, @clazz, 1.to_s
83
+ end
84
+ end
85
+
86
+ context "when any other exception is thrown" do
87
+ it "should log exception" do
88
+ @model = Executor.make :max_tasks => 1, :schedulable => true
89
+ @model.send :startup
90
+
91
+ @model.send(:log).should_receive(:debug).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "for execution (one time)")
92
+ @model.send(:log).should_receive(:error).with log_message(@model.name, "preparing", 1.to_s, @clazz.class.name, "encountered an unexpected exception. java.lang.IllegalArgumentException: No enum const class java.util.concurrent.TimeUnit.RANDOM")
93
+
94
+ @model.send :execute, @clazz, 1.to_s, nil, nil, nil, "random"
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "#shutdown" do
100
+ before(:each) { @model.send :startup }
101
+
102
+ it "should shutdown executor" do
103
+ @model.send(:log).should_receive(:debug).with log_statement(@model.name, "shutdown triggered")
104
+ @model.send(:executor).should_receive :shutdown
105
+ @model.send(:log).should_receive(:info).with log_statement(@model.name, "shutdown")
106
+
107
+ @model.send :shutdown
108
+
109
+ @model.send(:executor).should be_nil
110
+ end
111
+
112
+ context "when security exception is thrown" do
113
+ it "should force shutdown executor" do
114
+ @model.send(:log).should_receive(:debug).with log_statement(@model.name, "shutdown triggered")
115
+ @model.send(:executor).should_receive(:shutdown).and_raise Java::java.lang.SecurityException.new
116
+ @model.send(:log).should_receive(:warn).with log_statement(@model.name, "shutdown encountered a security exception")
117
+ @model.should_receive :forced_shutdown
118
+
119
+ @model.send :shutdown
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "#forced_shutdown" do
125
+ before(:each) { @model.send :startup }
126
+
127
+ it "should shutdown executor" do
128
+ @model.send(:log).should_receive(:debug).with log_statement(@model.name, "forced shutdown triggered")
129
+ @model.send(:executor).should_receive :shutdown_now
130
+ @model.send(:log).should_receive(:info).with log_statement(@model.name, "forced shutdown")
131
+
132
+ @model.send :forced_shutdown
133
+
134
+ @model.send(:executor).should be_nil
135
+ end
136
+
137
+ context "when security exception is thrown" do
138
+ it "should log exception" do
139
+ @model.send(:log).should_receive(:debug).with log_statement(@model.name, "forced shutdown triggered")
140
+ @model.send(:executor).should_receive(:shutdown_now).and_raise Java::java.lang.SecurityException.new
141
+ @model.send(:log).should_receive(:error).with log_statement(@model.name, "forced shutdown encountered a security exception")
142
+ @model.send(:log).should_receive(:fatal).with log_statement(@model.name, "forced shutdown failure")
143
+
144
+ @model.send :forced_shutdown
145
+
146
+ @model.send(:executor).should be_nil
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,70 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsExecutor::Executor::Model::InstanceSupportMethods do
4
+ before(:each) { @model = Executor.make }
5
+
6
+ it { @model.should_not allow_public_access_for_methods :executor, :executor=, :startupable?, :shutdownable?, :log }
7
+
8
+ describe "#executor" do
9
+ it "should return executor" do
10
+ executor = double "Executor"
11
+ @model.send :executor=, executor
12
+
13
+ @model.send(:executor).should == executor
14
+ end
15
+
16
+ context "when id has not been set" do
17
+ it "should raise an argument error" do
18
+ @model.id = nil
19
+
20
+ expect { @model.send :executor=, true }.to raise_error ArgumentError, "cannot reference executor against nil id"
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#startupable?" do
26
+ context "when rails initialized" do
27
+ before(:each) { ActsAsExecutor.should_receive(:rails_initialized?).at_most(:once).and_return true }
28
+
29
+ context "when executor has not been set" do
30
+ it "should return true" do
31
+ @model.should be_startupable
32
+ end
33
+ end
34
+
35
+ context "when executor has been set" do
36
+ it "should return false" do
37
+ @model.send :startup
38
+
39
+ @model.should_not be_startupable
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#shutdownable?" do
46
+ context "when rails initialized" do
47
+ before(:each) { ActsAsExecutor.should_receive(:rails_initialized?).at_most(:once).and_return true }
48
+
49
+ context "when executor has been set" do
50
+ it "should return true" do
51
+ @model.send :startup
52
+
53
+ @model.should be_shutdownable
54
+ end
55
+ end
56
+
57
+ context "when executor has not been set" do
58
+ it "should return false" do
59
+ @model.should_not be_shutdownable
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#log" do
66
+ it "should return class log" do
67
+ @model.send(:log).should == Executor.log
68
+ end
69
+ end
70
+ end