acts_as_executor 1.0.0.beta2 → 1.0.0.rc1

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 (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
@@ -10,8 +10,15 @@ Gem::Specification.new do |s|
10
10
  s.email = ["philostler@gmail.com"]
11
11
  s.homepage = "https://github.com/philostler/acts_as_executor"
12
12
  s.summary = %q{Java Executor framework integration for Rails}
13
- s.description = %q{Seamless integration of Java's Executor framework with JRuby on Rails}
13
+ s.description = %q{Seamlessly integrates Java's Executor framework with JRuby on Rails}
14
14
 
15
15
  s.files = Dir["**/*.rb"] + Dir["*.rdoc"] + Dir["LICENSE"] + Dir["*.gemspec"]
16
16
  s.require_paths = ["lib"]
17
+
18
+ s.add_dependency "activemodel", ">= 3.0"
19
+ s.add_dependency "activerecord", ">= 3.0"
20
+
21
+ s.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 1.1"
22
+ s.add_development_dependency "machinist", "2.0.0.beta2"
23
+ s.add_development_dependency "rspec", "~> 2.6"
17
24
  end
@@ -1,46 +1,33 @@
1
+ require "active_model"
2
+ require "active_record"
3
+
4
+ require "acts_as_executor/version"
5
+
1
6
  require "acts_as_executor/common/future_task"
7
+ require "acts_as_executor/common/instance_support_methods"
2
8
  require "acts_as_executor/common/units"
3
9
 
4
10
  require "acts_as_executor/executor/factory"
5
- require "acts_as_executor/executor/kinds"
6
11
 
7
- require "acts_as_executor/executor/model/actions"
8
- require "acts_as_executor/executor/model/associations"
9
- require "acts_as_executor/executor/model/attributes"
10
- require "acts_as_executor/executor/model/base"
11
- require "acts_as_executor/executor/model/helpers"
12
- require "acts_as_executor/executor/model/validations"
12
+ require "acts_as_executor/executor/model/class_methods"
13
+ require "acts_as_executor/executor/model/instance_methods"
14
+ require "acts_as_executor/executor/model/instance_support_methods"
13
15
 
16
+ require "acts_as_executor/task/clazz"
14
17
  require "acts_as_executor/task/schedules"
15
18
 
16
- require "acts_as_executor/task/model/actions"
17
- require "acts_as_executor/task/model/associations"
18
- require "acts_as_executor/task/model/attributes"
19
- require "acts_as_executor/task/model/base"
20
- require "acts_as_executor/task/model/helpers"
21
- require "acts_as_executor/task/model/validations"
19
+ require "acts_as_executor/task/model/class_methods"
20
+ require "acts_as_executor/task/model/instance_methods"
21
+ require "acts_as_executor/task/model/instance_support_methods"
22
22
 
23
23
  require "acts_as_executor/validators/class_exists_validator"
24
+ require "acts_as_executor/validators/class_includes_validator"
24
25
 
25
- ActiveRecord::Base.send :extend, ActsAsExecutor::Executor::Model::Base
26
- ActiveRecord::Base.send :extend, ActsAsExecutor::Task::Model::Base
26
+ ActiveRecord::Base.send :extend, ActsAsExecutor::Executor::Model::ClassMethods
27
+ ActiveRecord::Base.send :extend, ActsAsExecutor::Task::Model::ClassMethods
27
28
 
28
29
  module ActsAsExecutor
29
- def self.log
30
- logger
31
- end
32
- def self.logger
33
- @@logger ||= Rails.logger
34
- end
35
- def self.logger= logger
36
- @@logger = logger
37
- end
38
-
39
- def self.configure
40
- yield self
41
- end
42
-
43
- def self.rails_startup?
44
- File.basename($0) == "rails"
30
+ def self.rails_initialized?
31
+ File.basename($0) != "rake"
45
32
  end
46
33
  end
@@ -1,10 +1,10 @@
1
1
  module ActsAsExecutor
2
2
  module Common
3
3
  class FutureTask < Java::java.util.concurrent.FutureTask
4
- attr_accessor :task
4
+ attr_writer :done_handler
5
5
 
6
6
  def done
7
- task.destroy
7
+ @done_handler.call if @done_handler
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,15 @@
1
+ module ActsAsExecutor
2
+ module Common
3
+ module InstanceSupportMethods
4
+ private
5
+ def log_statement executor_name, statement
6
+ "\"" + executor_name + "\" " + statement
7
+ end
8
+
9
+ def log_message executor_name, doing, task_id, clazz_name, message = ""
10
+ if message.kind_of? Hash then message = "with \"" + message.inspect + "\"" end
11
+ "\"" + executor_name + "\" " + doing + " \"" + task_id + "#" + clazz_name + "\" " + message
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,30 +1,26 @@
1
1
  module ActsAsExecutor
2
2
  module Executor
3
3
  class Factory
4
- def self.create kind, size = nil
5
- case kind
6
- when ActsAsExecutor::Executor::Kinds::CACHED
7
- return Java::java.util.concurrent.Executors.new_cached_thread_pool
8
-
9
- when ActsAsExecutor::Executor::Kinds::FIXED
10
- raise TypeError, "size cannot be nil" unless size
11
- raise ArgumentError, "size must be larger than 0" unless size.to_i > 0
12
- return Java::java.util.concurrent.Executors.new_fixed_thread_pool size.to_i
13
-
14
- when ActsAsExecutor::Executor::Kinds::SINGLE
15
- return Java::java.util.concurrent.Executors.new_single_thread_executor
16
-
17
- when ActsAsExecutor::Executor::Kinds::SCHEDULED
18
- raise TypeError, "size cannot be nil" unless size
19
- raise ArgumentError, "size must be larger than 0" unless size.to_i > 0
20
- return Java::java.util.concurrent.Executors.new_scheduled_thread_pool size.to_i
21
-
22
- when ActsAsExecutor::Executor::Kinds::SINGLE_SCHEDULED
23
- return Java::java.util.concurrent.Executors.new_single_thread_scheduled_executor
24
-
4
+ def self.create max_tasks, schedulable
5
+ executor = nil
6
+ if schedulable
7
+ if max_tasks == nil
8
+ # No Cached Scheduled Implementation
9
+ elsif max_tasks > 1
10
+ executor = Java::java.util.concurrent.Executors.new_scheduled_thread_pool max_tasks.to_i # Scheduled
25
11
  else
26
- return nil
12
+ executor = Java::java.util.concurrent.Executors.new_single_thread_scheduled_executor # Single Scheduled
13
+ end
14
+ else
15
+ if max_tasks == nil
16
+ executor = Java::java.util.concurrent.Executors.new_cached_thread_pool # Cached
17
+ elsif max_tasks > 1
18
+ executor = Java::java.util.concurrent.Executors.new_fixed_thread_pool max_tasks.to_i # Fixed
19
+ else
20
+ executor = Java::java.util.concurrent.Executors.new_single_thread_executor # Single
21
+ end
27
22
  end
23
+ executor
28
24
  end
29
25
  end
30
26
  end
@@ -0,0 +1,33 @@
1
+ module ActsAsExecutor
2
+ module Executor
3
+ module Model
4
+ module ClassMethods
5
+ def acts_as_executor *arguments
6
+ send :include, ActsAsExecutor::Common::InstanceSupportMethods
7
+ send :include, ActsAsExecutor::Executor::Model::InstanceMethods
8
+ send :include, ActsAsExecutor::Executor::Model::InstanceSupportMethods
9
+
10
+ hash = arguments.last.is_a?(Hash) ? arguments.pop : {}
11
+ self.log = hash[:log]
12
+
13
+ if ActsAsExecutor.rails_initialized?
14
+ all
15
+ at_exit do
16
+ all.each do |e|
17
+ e.send :shutdown
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def log
24
+ @@log ? @@log : Rails.logger
25
+ end
26
+
27
+ def log= log
28
+ @@log = log
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,87 @@
1
+ module ActsAsExecutor
2
+ module Executor
3
+ module Model
4
+ module InstanceMethods
5
+ def self.included base
6
+ # Associations
7
+ base.has_many :tasks, :class_name => base.table_name.singularize.camelize + "Task", :foreign_key => "executor_id"
8
+
9
+ # Callbacks
10
+ base.after_find :startup, :if => :startupable?
11
+ base.after_save :startup, :if => :startupable?
12
+ base.after_destroy :shutdown, :if => :shutdownable?
13
+
14
+ # Validations
15
+ base.validates :name, :presence => true, :uniqueness => true
16
+ base.validates :max_tasks, :numericality => { :only_integer => true, :greater_than_or_equal_to => 1 }, :allow_nil => true
17
+ end
18
+
19
+ private
20
+ def startup
21
+ log.debug log_statement name, "startup triggered"
22
+ self.executor = ActsAsExecutor::Executor::Factory.create max_tasks, schedulable
23
+ log.info log_statement name, "started"
24
+
25
+ tasks.all
26
+ end
27
+
28
+ def execute clazz, task_id, schedule = nil, start = nil, every = nil, units = nil
29
+ begin
30
+ humanized_schedule = schedule ? schedule.gsub("_", " ") : "one time"
31
+ log.debug log_message name, "preparing", task_id, clazz.class.name, "for execution (" + humanized_schedule + ")"
32
+
33
+ if schedulable?
34
+ units = Java::java.util.concurrent.TimeUnit.value_of(units.upcase)
35
+ case schedule
36
+ when ActsAsExecutor::Task::Schedules::ONE_SHOT
37
+ future = executor.schedule clazz, start, units
38
+ when ActsAsExecutor::Task::Schedules::FIXED_DELAY
39
+ future = executor.schedule_with_fixed_delay clazz, start, every, units
40
+ when ActsAsExecutor::Task::Schedules::FIXED_RATE
41
+ future = executor.schedule_at_fixed_rate clazz, start, every, units
42
+ end
43
+ else
44
+ future = ActsAsExecutor::Common::FutureTask.new clazz, nil
45
+ executor.execute future
46
+ end
47
+
48
+ log.info log_message name, "enqueued", task_id, clazz.class.name
49
+ future
50
+ rescue Java::java.util.concurrent.RejectedExecutionException
51
+ log.warn log_message name, "preparing", task_id, clazz.class.name, "encountered a rejected execution exception"
52
+ rescue Exception => exception
53
+ log.error log_message name, "preparing", task_id, clazz.class.name, "encountered an unexpected exception. " + exception
54
+ end
55
+ end
56
+
57
+ def shutdown
58
+ log.debug log_statement name, "shutdown triggered"
59
+ begin
60
+ executor.shutdown
61
+ rescue Java::java.lang.SecurityException
62
+ log.warn log_statement name, "shutdown encountered a security exception"
63
+ forced_shutdown
64
+ else
65
+ log.info log_statement name, "shutdown"
66
+ ensure
67
+ self.executor = nil
68
+ end
69
+ end
70
+
71
+ def forced_shutdown
72
+ log.debug log_statement name, "forced shutdown triggered"
73
+ begin
74
+ executor.shutdown_now
75
+ rescue Java::java.lang.SecurityException
76
+ log.error log_statement name, "forced shutdown encountered a security exception"
77
+ log.fatal log_statement name, "forced shutdown failure"
78
+ else
79
+ log.info log_statement name, "forced shutdown"
80
+ ensure
81
+ self.executor = nil
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,36 @@
1
+ module ActsAsExecutor
2
+ module Executor
3
+ module Model
4
+ module InstanceSupportMethods
5
+ private
6
+ @@executors = Hash.new
7
+ def executor
8
+ @@executors[id]
9
+ end
10
+
11
+ def executor= executor
12
+ raise ArgumentError, "cannot reference executor against nil id" unless id
13
+ @@executors[id] = executor
14
+ end
15
+
16
+ def startupable?
17
+ if executor == nil && ActsAsExecutor.rails_initialized?
18
+ return true
19
+ end
20
+ return false
21
+ end
22
+
23
+ def shutdownable?
24
+ if executor != nil && ActsAsExecutor.rails_initialized?
25
+ return true
26
+ end
27
+ return false
28
+ end
29
+
30
+ def log
31
+ self.class.log
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Clazz
4
+ include Java::java.lang.Runnable
5
+
6
+ attr_accessor :arguments
7
+ attr_writer :uncaught_exception_handler
8
+
9
+ private
10
+ def run
11
+ begin
12
+ if @arguments
13
+ @arguments.each_pair do |key, value|
14
+ class_eval { attr_accessor key } unless respond_to? key
15
+ send "#{key}=", value
16
+ end
17
+ end
18
+
19
+ execute
20
+ rescue Exception => exception
21
+ @uncaught_exception_handler.call exception if @uncaught_exception_handler
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Model
4
+ module ClassMethods
5
+ def acts_as_executor_task
6
+ send :include, ActsAsExecutor::Common::InstanceSupportMethods
7
+ send :include, ActsAsExecutor::Task::Model::InstanceMethods
8
+ send :include, ActsAsExecutor::Task::Model::InstanceSupportMethods
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Model
4
+ module InstanceMethods
5
+ def self.included base
6
+ # Associations
7
+ base.belongs_to :executor, :class_name => base.table_name.sub(/_tasks/, "").camelize
8
+
9
+ # Callbacks
10
+ base.after_find :enqueue, :if => :enqueueable?
11
+ base.after_save :enqueue, :if => :enqueueable?
12
+ base.after_destroy :cancel, :if => :cancelable?
13
+
14
+ # Serialization
15
+ base.serialize :arguments, Hash
16
+
17
+ # Validations
18
+ base.validates :executor_id, :presence => true
19
+ base.validates :clazz, :presence => true, :class_exists => true, :class_includes => { :includes => ActsAsExecutor::Task::Clazz }
20
+ base.validates :schedule, :inclusion => { :in => ActsAsExecutor::Task::Schedules::ALL }, :if => "executor and executor.schedulable?"
21
+ base.validates :start, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }, :if => "executor and executor.schedulable?"
22
+ base.validates :every, :numericality => { :only_integer => true, :greater_than_or_equal_to => 1 }, :if => "executor and executor.schedulable? and schedule != ActsAsExecutor::Task::Schedules::ONE_SHOT"
23
+ base.validates :units, :inclusion => { :in => ActsAsExecutor::Common::Units::ALL }, :if => "executor and executor.schedulable?"
24
+ end
25
+
26
+ private
27
+ def enqueue
28
+ begin
29
+ executor.send(:log).debug log_message executor.name, "creating", id.to_s, clazz, arguments
30
+
31
+ instance = instantiate clazz, arguments
32
+
33
+ self.future = executor.send(:execute, instance, id.to_s, schedule, start, every, units)
34
+ future.done_handler = method :done_handler
35
+ rescue Exception => exception
36
+ executor.send(:log).error log_message executor.name, "creating", id.to_s, clazz, "encountered an unexpected exception. " + exception
37
+ end
38
+ end
39
+
40
+ def instantiate class_name, arguments
41
+ instance = Object.const_get(class_name).new
42
+ instance.arguments = arguments
43
+ instance.uncaught_exception_handler = method :uncaught_exception_handler
44
+ instance
45
+ end
46
+
47
+ def done_handler
48
+ executor.send(:log).debug log_message executor.name, "completed", id.to_s, clazz
49
+ destroy
50
+ end
51
+
52
+ def uncaught_exception_handler exception
53
+ executor.send(:log).error log_message executor.name, "executing", id.to_s, clazz, "encountered an uncaught exception. " + exception
54
+ end
55
+
56
+ def cancel
57
+ future.cancel true
58
+
59
+ self.future = nil
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,32 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Model
4
+ module InstanceSupportMethods
5
+ private
6
+ @@futures = Hash.new
7
+ def future
8
+ @@futures[id]
9
+ end
10
+
11
+ def future= future
12
+ raise ArgumentError, "cannot reference future against nil id" unless id
13
+ @@futures[id] = future
14
+ end
15
+
16
+ def enqueueable?
17
+ if future == nil
18
+ return true
19
+ end
20
+ return false
21
+ end
22
+
23
+ def cancelable?
24
+ if future != nil
25
+ return true
26
+ end
27
+ return false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end