arsenicum 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/arsenicum.gemspec +5 -0
  4. data/bin/arsenicum +1 -7
  5. data/lib/arsenicum.rb +23 -12
  6. data/lib/arsenicum/async.rb +3 -0
  7. data/lib/arsenicum/async/queue.rb +37 -0
  8. data/lib/arsenicum/async/queue/sqs.rb +27 -0
  9. data/lib/arsenicum/configuration.rb +114 -63
  10. data/lib/arsenicum/core.rb +6 -0
  11. data/lib/arsenicum/core/broker.rb +98 -0
  12. data/lib/arsenicum/core/commands.rb +15 -0
  13. data/lib/arsenicum/core/io_helper.rb +34 -0
  14. data/lib/arsenicum/core/worker.rb +91 -0
  15. data/lib/arsenicum/formatter.rb +127 -0
  16. data/lib/arsenicum/io.rb +3 -0
  17. data/lib/arsenicum/main.rb +23 -0
  18. data/lib/arsenicum/routing.rb +3 -0
  19. data/lib/arsenicum/routing/default.rb +3 -0
  20. data/lib/arsenicum/serializer.rb +3 -0
  21. data/lib/arsenicum/serializer/json.rb +11 -0
  22. data/lib/arsenicum/task.rb +10 -50
  23. data/lib/arsenicum/task/class_dispatcher.rb +15 -0
  24. data/lib/arsenicum/util.rb +48 -0
  25. data/lib/arsenicum/version.rb +1 -1
  26. data/lib/generators/arsenicum/migration/migration_generator.rb +32 -0
  27. data/lib/generators/arsenicum/migration/templates/active_record/migration.rb +15 -0
  28. data/spec/arsenicum/queueing/post_office_spec.rb +123 -0
  29. data/spec/arsenicum/queueing/serializer_spec.rb +70 -0
  30. data/spec/config/config.example.yml +22 -0
  31. data/spec/config/post_office_spec.yml +24 -0
  32. data/spec/rails_project/.gitignore +16 -0
  33. data/spec/rails_project/Gemfile +45 -0
  34. data/spec/rails_project/README.rdoc +28 -0
  35. data/spec/rails_project/Rakefile +6 -0
  36. data/spec/rails_project/app/assets/images/.keep +0 -0
  37. data/spec/rails_project/app/assets/javascripts/application.js +16 -0
  38. data/spec/rails_project/app/assets/stylesheets/application.css +13 -0
  39. data/spec/rails_project/app/controllers/application_controller.rb +5 -0
  40. data/spec/rails_project/app/controllers/concerns/.keep +0 -0
  41. data/spec/rails_project/app/helpers/application_helper.rb +2 -0
  42. data/spec/rails_project/app/mailers/.keep +0 -0
  43. data/spec/rails_project/app/models/.keep +0 -0
  44. data/spec/rails_project/app/models/concerns/.keep +0 -0
  45. data/spec/rails_project/app/models/sample.rb +2 -0
  46. data/spec/rails_project/app/views/layouts/application.html.erb +14 -0
  47. data/spec/rails_project/bin/bundle +3 -0
  48. data/spec/rails_project/bin/rails +4 -0
  49. data/spec/rails_project/bin/rake +4 -0
  50. data/spec/rails_project/config.ru +4 -0
  51. data/spec/rails_project/config/application.rb +23 -0
  52. data/spec/rails_project/config/boot.rb +4 -0
  53. data/spec/rails_project/config/database.yml +25 -0
  54. data/spec/rails_project/config/environment.rb +5 -0
  55. data/spec/rails_project/config/environments/development.rb +29 -0
  56. data/spec/rails_project/config/environments/production.rb +80 -0
  57. data/spec/rails_project/config/environments/test.rb +36 -0
  58. data/spec/rails_project/config/initializers/backtrace_silencers.rb +7 -0
  59. data/spec/rails_project/config/initializers/filter_parameter_logging.rb +4 -0
  60. data/spec/rails_project/config/initializers/inflections.rb +16 -0
  61. data/spec/rails_project/config/initializers/mime_types.rb +5 -0
  62. data/spec/rails_project/config/initializers/secret_token.rb +12 -0
  63. data/spec/rails_project/config/initializers/session_store.rb +3 -0
  64. data/spec/rails_project/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/rails_project/config/locales/en.yml +23 -0
  66. data/spec/rails_project/config/routes.rb +56 -0
  67. data/spec/rails_project/db/migrate/20131210025434_create_samples.rb +9 -0
  68. data/spec/rails_project/db/schema.rb +22 -0
  69. data/spec/rails_project/db/seeds.rb +7 -0
  70. data/spec/rails_project/lib/assets/.keep +0 -0
  71. data/spec/rails_project/lib/tasks/.keep +0 -0
  72. data/spec/rails_project/log/.keep +0 -0
  73. data/spec/rails_project/public/404.html +58 -0
  74. data/spec/rails_project/public/422.html +58 -0
  75. data/spec/rails_project/public/500.html +57 -0
  76. data/spec/rails_project/public/favicon.ico +0 -0
  77. data/spec/rails_project/public/robots.txt +5 -0
  78. data/spec/rails_project/test/controllers/.keep +0 -0
  79. data/spec/rails_project/test/helpers/.keep +0 -0
  80. data/spec/rails_project/test/integration/.keep +0 -0
  81. data/spec/rails_project/test/mailers/.keep +0 -0
  82. data/spec/rails_project/test/test_helper.rb +15 -0
  83. data/spec/rails_project/vendor/assets/javascripts/.keep +0 -0
  84. data/spec/rails_project/vendor/assets/stylesheets/.keep +0 -0
  85. data/spec/spec_helper.rb +5 -0
  86. metadata +217 -27
  87. data/lib/arsenicum/actor.rb +0 -14
  88. data/lib/arsenicum/cli.rb +0 -54
  89. data/lib/arsenicum/cli/rails.rb +0 -26
  90. data/lib/arsenicum/queue.rb +0 -58
  91. data/lib/arsenicum/queue_proxy.rb +0 -73
  92. data/lib/arsenicum/rake_tasks.rake +0 -24
  93. data/lib/arsenicum/serialization.rb +0 -110
  94. data/lib/arsenicum/server.rb +0 -40
  95. data/lib/arsenicum/sqs.rb +0 -5
  96. data/lib/arsenicum/sqs/queue.rb +0 -76
  97. data/lib/arsenicum/syntax.rb +0 -11
  98. data/lib/arsenicum/syntax/delayed_job.rb +0 -25
  99. data/lib/arsenicum/watchdog.rb +0 -68
  100. data/spec/config.example.yml +0 -21
@@ -1,14 +0,0 @@
1
- module Arsenicum
2
- class Actor
3
- attr_reader :queue
4
-
5
- def initialize(queue)
6
- @queue = queue
7
- end
8
-
9
- def process(task)
10
- task.execute
11
- queue.update_message_status(task.message_id, task.successful?)
12
- end
13
- end
14
- end
@@ -1,54 +0,0 @@
1
- require 'optparse'
2
- require 'yaml'
3
-
4
- module Arsenicum
5
- class CLI
6
- autoload :Rails, 'arsenicum/cli/rails'
7
-
8
- attr_reader :configuration
9
-
10
- def initialize(argv)
11
- @configuration = option_parser.parse!(argv)
12
- end
13
-
14
- def boot
15
- Arsenicum::Server.start(Arsenicum::Configuration.new(configuration))
16
- end
17
-
18
- class OptionParser
19
- def initialize
20
- @values = {}
21
- @parser = ::OptionParser.new
22
- end
23
-
24
- def register(*args)
25
- block = args.pop
26
- block = block.to_proc unless block.is_a? Proc
27
-
28
- if block.arity == 2
29
- @parser.on(*args) {|v|block.call(v, @values)}
30
- else
31
- @parser.on(*args) {|v|@values.merge! block.call(v)}
32
- end
33
- self
34
- end
35
-
36
- def parse!(argv)
37
- @parser.parse! argv
38
- @values
39
- end
40
- end
41
-
42
- private
43
- def option_parser
44
- OptionParser.new.
45
- register("-c", "--config-file=YAML", -> v {YAML.load(File.read(v, encoding: "UTF-8"))}).
46
- register("-t", "--default-concurrency=VALUE", -> v {{default_concurrency: v.to_i}}).
47
- register("-q", "--queue-type=QUEUE_TYPE", -> v{{queue_type: v.to_s}}).
48
- register("--queue-engine-config=CONFIGKEY_VALUE", -> v, config {
49
- config[:engine_config] ||= {};(key, value) = v.split(':');config[:engine_config][key.to_sym] = value.to_s
50
- })
51
- end
52
-
53
- end
54
- end
@@ -1,26 +0,0 @@
1
- module Arsenicum
2
- class CLI::Rails < CLI
3
- def boot
4
- ENV['RACK_ENV'] = (ENV['RAILS_ENV'] ||= (configuration[:rails_env] || 'development'))
5
- rootdir = ENV['RAILS_ROOT'] || configuration[:dir] || Dir.pwd
6
- Dir.chdir rootdir
7
-
8
- if configuration[:background] && !configuration[:pidfile]
9
- configuration[:pidfile] = "#{rootdir}/tmp/pids/arsenicum.pid"
10
- end
11
-
12
- load File.join(rootdir, 'config/environment.rb')
13
- Arsenicum::Configuration.reconfigure configuration
14
- Arsenicum::Server.start
15
- end
16
-
17
- def option_parser
18
- OptionParser.new.register("-e", "--environment=ENVIRONMENT", -> v { {rails_env: v} }).
19
- register("-d", "--dir=DIRECTORY", -> v { {dir: v} }).
20
- register("-p", "--pidfile=PID_FILE", -> v { { pidfile: v } }).
21
- register("-l", "--log-file=LOG_FILE", -> v { { log_file: v } }).
22
- register("-D", "--daemon", -> v { { background: true } }).
23
- register("-L", "--log-level=LOG_LEVEL", -> v { {log_level: v} })
24
- end
25
- end
26
- end
@@ -1,58 +0,0 @@
1
- require 'json'
2
-
3
- module Arsenicum
4
- class Queue
5
- DEFAULT_CONCURRENCY = 5
6
-
7
- attr_reader :name, :concurrency, :queue_methods, :queue_classes, :logger
8
-
9
- def initialize(name, config = {}, logger = nil)
10
- @name = name
11
- @concurrency = (config.delete(:concurrency) || DEFAULT_CONCURRENCY).to_i
12
- @queue_methods = config.delete(:methods)
13
- @queue_classes = config.delete(:classes)
14
- @logger = logger || Logger.new(STDOUT)
15
- configure(config)
16
- end
17
-
18
- def put(hash)
19
- json = JSON(hash.merge(timestamp: (Time.now.to_f * 1000000).to_i.to_s))
20
- logger.debug { "Queue Put[#{name}] values #{json}" }
21
- put_to_queue(json)
22
- end
23
-
24
- ######################################################
25
- #
26
- # Queue must implement the methods as below:
27
- # 1. put_to_queue(json): putting the actual message
28
- # into the queue backend. The argument of this
29
- # method will be the JSON string.
30
- # 2. poll: polling the queue and retrieve the
31
- # message information. This method is expected to
32
- # return the message Hash. Its keys and values
33
- # are expected as below:
34
- # :message_body: the raw string that was pushed
35
- # via the :put_to_queue method.
36
- # :message_id: the identifier of this message.
37
- # This is usually used to update the status of
38
- # message on the queue backend.
39
- # 3. update_message_status(message_id, successful, json):
40
- # Update the status of the message. Arguments are
41
- # as following:
42
- # message_id: The identifier of the message to
43
- # be updated. This value will be set as the
44
- # :message_id of the return value of :poll.
45
- # successful: The result of the process done.
46
- # If the process complete successfully,
47
- # this argument will be set true. Otherwise,
48
- # this will be false.
49
- # json: the message received.
50
- # 4. create_queue_backend - optional
51
- # Register the queue itself on its backend.
52
- # This will be invoked from the rake task
53
- # 'arsenicum:create_queues'.
54
- # Note: this method should be implemented idempotently.
55
- #
56
- #####################################################
57
- end
58
- end
@@ -1,73 +0,0 @@
1
- module Arsenicum
2
- class QueueProxy
3
- include Serialization
4
-
5
- def self.instance
6
- @instance ||= new(Arsenicum::Configuration.instance)
7
- end
8
-
9
- attr_reader :configuration
10
- attr_reader :queues
11
- attr_reader :default_queue
12
- attr_reader :method_queue_tables
13
- attr_reader :class_queue_tables
14
-
15
- def initialize(configuration)
16
- @configuration = configuration
17
- queue_class = configuration.queue_class
18
- @method_queue_tables = {}
19
- @class_queue_tables = {}
20
-
21
- @queues = configuration.queue_configurations.inject({}) do |h, kv|
22
- (queue_name, queue_configuration) = kv
23
- queue = queue_class.new(queue_name, queue_configuration.merge(configuration.engine_configuration))
24
- Array(queue.queue_methods).tap(&:compact!).each do |m|
25
- method_queue_tables[m] ||= queue
26
- end
27
- Array(queue.queue_classes).tap(&:compact!).each do |m|
28
- class_queue_tables[m] ||= queue
29
- end
30
- h[queue_name] = queue
31
-
32
- h
33
- end
34
- @default_queue = queues[:default]
35
- end
36
-
37
- def async(target, method, *arguments)
38
- values = {
39
- target: prepare_serialization(target),
40
- method_name: method,
41
- arguments: arguments.map{|arg|prepare_serialization(arg)},
42
- }
43
- specify_queue(target, method).
44
- tap{|q|logger.debug { "Queue #{q.name}: Param #{values.inspect}" }}.
45
- put(values)
46
- end
47
-
48
- def logger
49
- configuration.logger
50
- end
51
-
52
- private
53
- def specify_queue(target, method)
54
- if target.is_a?(Module)
55
- conjunction = '.'
56
- klass = target
57
- else
58
- conjunction = '#'
59
- klass = target.class
60
- end
61
- method_signature = [target.class.name, method].join conjunction
62
- if queue = method_queue_tables[method_signature]
63
- return queue
64
- end
65
- klass_signature = target.class.name
66
- if queue = class_queue_tables[klass_signature]
67
- return queue
68
- end
69
-
70
- return default_queue
71
- end
72
- end
73
- end
@@ -1,24 +0,0 @@
1
- require 'arsenicum'
2
- require 'yaml'
3
-
4
- namespace :arsenicum do
5
- desc 'Create queues defined in the configuration file. Specify configuration with CONFIG=config_file_path.'
6
- task :create_queues do
7
- config_file = ENV['CONFIG'] || 'config/arsenicum.yml'
8
- yaml = YAML.load(Erubis::Eruby.new(File.read(config_file, encoding: 'UTF-8')).result)
9
- config_values =
10
- if ENV['CONFIG_KEY']
11
- ENV['CONFIG_KEY'].split('.').inject(yaml) do |values, key|
12
- values[key]
13
- end
14
- else
15
- yaml
16
- end
17
-
18
- config = Arsenicum::Configuration.new(config_values)
19
- queue_class = config.queue_class
20
- raise Arsenicum::MisconfigurationError, "class #{queue_class.name} doesn't support create_queue" unless queue_class.instance_methods.include?(:create_queue_backend)
21
-
22
- config.create_queues.each(&:create_queue_backend)
23
- end
24
- end
@@ -1,110 +0,0 @@
1
- require 'date'
2
- require 'time'
3
-
4
- module Arsenicum
5
- module Serialization
6
- DATE_FORMAT = "%Y-%m-%d".freeze
7
- DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S %Z %z".freeze
8
-
9
- def prepare_serialization(value)
10
- hash_value = prepare_serialization_specific(value) || prepare_serialization_default(value)
11
- end
12
-
13
- def prepare_serialization_specific(value)
14
- case value
15
- when Integer, Float, String, TrueClass, FalseClass, NilClass
16
- {
17
- type: :raw,
18
- value: value.inspect,
19
- }
20
- when Date
21
- {
22
- type: 'date',
23
- value: value.strftime(DATE_FORMAT),
24
- }
25
- when DateTime, Time
26
- {
27
- type: 'time',
28
- value: value.strftime(DATE_TIME_FORMAT),
29
- }
30
- when Class
31
- {
32
- type: :class,
33
- value: value.name,
34
- }
35
- when Array
36
- {
37
- type: :array,
38
- values: value.map{|v|serialize(v)},
39
- }
40
- when Hash
41
- {
42
- type: :hash,
43
- values: value.inject({}){|h, kv|(k,v)=kv;h[k.to_s]=serialize(v);h},
44
- }
45
- end
46
- end
47
-
48
- def prepare_serialization_default(value)
49
- {
50
- type: 'marshal',
51
- value: Marshal.dump(value).unpack('H*').first,
52
- }
53
- end
54
-
55
- module_function :prepare_serialization_specific, :prepare_serialization_default, :prepare_serialization
56
-
57
- module WithActiveRecord
58
- def self.included(base)
59
- base.module_eval do
60
- alias_method :prepare_serialization_specific_original, :prepare_serialization_specific
61
-
62
- def prepare_serialization_specific(value)
63
- prepare_serialization_specific_original(value) || prepare_serialization_active_record(value)
64
- end
65
-
66
- private
67
- def prepare_serialization_active_record(value)
68
- return {
69
- type: :active_record,
70
- class: value.class.name,
71
- id: value.id,
72
- } if value.is_a? ActiveRecord::Base
73
- end
74
-
75
- module_function :prepare_serialization_specific_original, :prepare_serialization_active_record
76
- end
77
- end
78
- end
79
-
80
- include(WithActiveRecord) if defined? ::ActiveRecord::Base
81
-
82
- def restore(value)
83
- case value['type']
84
- when 'raw'
85
- eval value['value']
86
- when 'date'
87
- Date.strptime(value['value'], DATE_FORMAT)
88
- when 'time'
89
- Time.strptime(value['value'], DATE_TIME_FORMAT)
90
- when 'class'
91
- Module.const_get value['value'].to_sym
92
- when 'active_record'
93
- klass = const_get value['class'].to_sym
94
- klass.find value['id']
95
- when 'array'
96
- value['values'].map do |value|
97
- restore(value)
98
- end
99
- when 'hash'
100
- value['values'].inject({}) do |h, key_value|
101
- (key, value) = key_value
102
- h[key.to_sym] = restore(key_value)
103
- h
104
- end
105
- else
106
- Marshal.restore [value['value']].pack('H*')
107
- end
108
- end
109
- end
110
- end
@@ -1,40 +0,0 @@
1
- module Arsenicum
2
- class Server
3
- attr_reader :watchdogs
4
- attr_reader :config
5
-
6
- def initialize(config)
7
- @config = config
8
- end
9
-
10
- def self.start(config = Arsenicum::Configuration.instance)
11
- Process.daemon(true, true) if config.background
12
- File.open(config.pidfile, 'w'){|f|f.puts $$} if config.pidfile
13
- new(config).start
14
- end
15
-
16
- def start
17
- puts "Booting Arsenicum Server..."
18
- Signal.trap(:INT, &method(:trap_interruption))
19
-
20
- queue_class = config.queue_class
21
- @watchdogs = config.create_queues.map do |queue|
22
- Arsenicum::WatchDog.new(queue, config.logger)
23
- end
24
- @watchdogs.each(&:boot)
25
-
26
- loop { sleep 10 }
27
- end
28
-
29
- def shutdown
30
- @watchdogs.each(&:shutdown)
31
- File.delete config.pidfile if config.pidfile
32
- Thread.current.terminate
33
- end
34
-
35
- private
36
- def trap_interruption(*)
37
- shutdown
38
- end
39
- end
40
- end
@@ -1,5 +0,0 @@
1
- module Arsenicum
2
- module Sqs
3
- autoload :Queue, 'arsenicum/sqs/queue'
4
- end
5
- end
@@ -1,76 +0,0 @@
1
- require 'aws-sdk'
2
-
3
- module Arsenicum::Sqs
4
- class Queue < Arsenicum::Queue
5
- attr_reader :account
6
- attr_reader :sqs
7
- attr_reader :wait_timeout
8
- attr_reader :failure_queue_name
9
- attr_reader :queue_configuration
10
-
11
- def configure(config)
12
- @account = config.delete :account
13
- @sqs = AWS::SQS.new account
14
- @wait_timeout =
15
- if config.delete(:long_poll)
16
- nil
17
- elsif timeout = config.delete(:wait_timeout)
18
- timeout.to_i
19
- end
20
- @failure_queue_name = config.delete :failure_queue_name
21
-
22
- @queue_configuration = config
23
- end
24
-
25
- def put_to_queue(json, named: name)
26
- sqs_queue = sqs.queues.named(named.to_s)
27
- sqs_queue.send_message(json)
28
- end
29
-
30
- def poll
31
- sqs.queues.named(name.to_s).poll(wait_time_out: wait_timeout) do |message|
32
- logger.debug { "RAW_MESSAGE #{message.inspect}" }
33
- {
34
- message_body: message.body,
35
- message_id: message.handle,
36
- }
37
- end
38
- end
39
-
40
- def update_message_status(message_id, successful, json)
41
- put_to_queue(json, named: failure_queue_name) unless successful
42
-
43
- sqs_queue = sqs.named(name)
44
- sqs.client.delete_message queue_url: sqs_queue.url, receipt_handle: message_id
45
- end
46
-
47
- CREATION_OPTIONS = [
48
- :visibility_timeout, :maximum_message_size,
49
- :delay_seconds, :message_retention_period,
50
- ].freeze
51
- CREATION_OPTIONS_IN_JSON = [
52
- :policy,
53
- ].freeze
54
-
55
- def create_queue_backend
56
- creation_options = queue_configuration.values_at(*CREATION_OPTIONS).
57
- each_with_index.inject({}) do |opts, vi|
58
-
59
- (value, i) = vi
60
- opts[CREATION_OPTIONS[i]] = value if value
61
- opts
62
- end
63
- CREATION_OPTIONS_IN_JSON.each do |opt|
64
- creation_options[opt] = JSON(queue_configuration[opt]) if queue_configuration[opt]
65
- end
66
-
67
- begin
68
- sqs.queues.named(name.to_s)
69
- puts "Not Created Queue #{name}:Exists"
70
- rescue AWS::SQS::Errors::NonExistentQueue
71
- sqs.queues.create name.to_s, creation_options
72
- puts "Created Queue #{name}"
73
- end
74
- end
75
- end
76
- end