arsenicum 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/arsenicum.gemspec +5 -0
- data/bin/arsenicum +1 -7
- data/lib/arsenicum.rb +23 -12
- data/lib/arsenicum/async.rb +3 -0
- data/lib/arsenicum/async/queue.rb +37 -0
- data/lib/arsenicum/async/queue/sqs.rb +27 -0
- data/lib/arsenicum/configuration.rb +114 -63
- data/lib/arsenicum/core.rb +6 -0
- data/lib/arsenicum/core/broker.rb +98 -0
- data/lib/arsenicum/core/commands.rb +15 -0
- data/lib/arsenicum/core/io_helper.rb +34 -0
- data/lib/arsenicum/core/worker.rb +91 -0
- data/lib/arsenicum/formatter.rb +127 -0
- data/lib/arsenicum/io.rb +3 -0
- data/lib/arsenicum/main.rb +23 -0
- data/lib/arsenicum/routing.rb +3 -0
- data/lib/arsenicum/routing/default.rb +3 -0
- data/lib/arsenicum/serializer.rb +3 -0
- data/lib/arsenicum/serializer/json.rb +11 -0
- data/lib/arsenicum/task.rb +10 -50
- data/lib/arsenicum/task/class_dispatcher.rb +15 -0
- data/lib/arsenicum/util.rb +48 -0
- data/lib/arsenicum/version.rb +1 -1
- data/lib/generators/arsenicum/migration/migration_generator.rb +32 -0
- data/lib/generators/arsenicum/migration/templates/active_record/migration.rb +15 -0
- data/spec/arsenicum/queueing/post_office_spec.rb +123 -0
- data/spec/arsenicum/queueing/serializer_spec.rb +70 -0
- data/spec/config/config.example.yml +22 -0
- data/spec/config/post_office_spec.yml +24 -0
- data/spec/rails_project/.gitignore +16 -0
- data/spec/rails_project/Gemfile +45 -0
- data/spec/rails_project/README.rdoc +28 -0
- data/spec/rails_project/Rakefile +6 -0
- data/spec/rails_project/app/assets/images/.keep +0 -0
- data/spec/rails_project/app/assets/javascripts/application.js +16 -0
- data/spec/rails_project/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_project/app/controllers/application_controller.rb +5 -0
- data/spec/rails_project/app/controllers/concerns/.keep +0 -0
- data/spec/rails_project/app/helpers/application_helper.rb +2 -0
- data/spec/rails_project/app/mailers/.keep +0 -0
- data/spec/rails_project/app/models/.keep +0 -0
- data/spec/rails_project/app/models/concerns/.keep +0 -0
- data/spec/rails_project/app/models/sample.rb +2 -0
- data/spec/rails_project/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_project/bin/bundle +3 -0
- data/spec/rails_project/bin/rails +4 -0
- data/spec/rails_project/bin/rake +4 -0
- data/spec/rails_project/config.ru +4 -0
- data/spec/rails_project/config/application.rb +23 -0
- data/spec/rails_project/config/boot.rb +4 -0
- data/spec/rails_project/config/database.yml +25 -0
- data/spec/rails_project/config/environment.rb +5 -0
- data/spec/rails_project/config/environments/development.rb +29 -0
- data/spec/rails_project/config/environments/production.rb +80 -0
- data/spec/rails_project/config/environments/test.rb +36 -0
- data/spec/rails_project/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_project/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_project/config/initializers/inflections.rb +16 -0
- data/spec/rails_project/config/initializers/mime_types.rb +5 -0
- data/spec/rails_project/config/initializers/secret_token.rb +12 -0
- data/spec/rails_project/config/initializers/session_store.rb +3 -0
- data/spec/rails_project/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_project/config/locales/en.yml +23 -0
- data/spec/rails_project/config/routes.rb +56 -0
- data/spec/rails_project/db/migrate/20131210025434_create_samples.rb +9 -0
- data/spec/rails_project/db/schema.rb +22 -0
- data/spec/rails_project/db/seeds.rb +7 -0
- data/spec/rails_project/lib/assets/.keep +0 -0
- data/spec/rails_project/lib/tasks/.keep +0 -0
- data/spec/rails_project/log/.keep +0 -0
- data/spec/rails_project/public/404.html +58 -0
- data/spec/rails_project/public/422.html +58 -0
- data/spec/rails_project/public/500.html +57 -0
- data/spec/rails_project/public/favicon.ico +0 -0
- data/spec/rails_project/public/robots.txt +5 -0
- data/spec/rails_project/test/controllers/.keep +0 -0
- data/spec/rails_project/test/helpers/.keep +0 -0
- data/spec/rails_project/test/integration/.keep +0 -0
- data/spec/rails_project/test/mailers/.keep +0 -0
- data/spec/rails_project/test/test_helper.rb +15 -0
- data/spec/rails_project/vendor/assets/javascripts/.keep +0 -0
- data/spec/rails_project/vendor/assets/stylesheets/.keep +0 -0
- data/spec/spec_helper.rb +5 -0
- metadata +217 -27
- data/lib/arsenicum/actor.rb +0 -14
- data/lib/arsenicum/cli.rb +0 -54
- data/lib/arsenicum/cli/rails.rb +0 -26
- data/lib/arsenicum/queue.rb +0 -58
- data/lib/arsenicum/queue_proxy.rb +0 -73
- data/lib/arsenicum/rake_tasks.rake +0 -24
- data/lib/arsenicum/serialization.rb +0 -110
- data/lib/arsenicum/server.rb +0 -40
- data/lib/arsenicum/sqs.rb +0 -5
- data/lib/arsenicum/sqs/queue.rb +0 -76
- data/lib/arsenicum/syntax.rb +0 -11
- data/lib/arsenicum/syntax/delayed_job.rb +0 -25
- data/lib/arsenicum/watchdog.rb +0 -68
- data/spec/config.example.yml +0 -21
@@ -0,0 +1,15 @@
|
|
1
|
+
module Arsenicum::Core::Commands
|
2
|
+
COMMAND_STOP = 0xff
|
3
|
+
COMMAND_TASK = 0x10
|
4
|
+
|
5
|
+
class << self
|
6
|
+
private
|
7
|
+
def code_to_string(code)
|
8
|
+
[code].pack('C')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
COMMAND_STRING_STOP = code_to_string(COMMAND_STOP)
|
13
|
+
COMMAND_STRING_TASK = code_to_string(COMMAND_TASK)
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Arsenicum::Core::IOHelper
|
2
|
+
def write_string(io, string)
|
3
|
+
string_to_write = string.dup
|
4
|
+
string_to_write.force_encoding 'BINARY'
|
5
|
+
|
6
|
+
io.write [string.length].pack('N')
|
7
|
+
io.write string
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_string(io, encoding: 'UTF-8')
|
11
|
+
bytes_for_length = read_from io, 4
|
12
|
+
length = bytes_for_length.unpack('N').first
|
13
|
+
return if length == 0
|
14
|
+
|
15
|
+
read_from(io, length).tap{|s|s.force_encoding encoding}
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_code(io, integer_value)
|
19
|
+
value = [integer_value].pack('C')
|
20
|
+
io.write value
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_code(io)
|
24
|
+
string = read_from io, 1
|
25
|
+
string.unpack('C').first
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def read_from(io, length)
|
30
|
+
bytes = io.read length
|
31
|
+
raise Arsenicum::IO::EOFException unless bytes
|
32
|
+
bytes
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
3
|
+
class Arsenicum::Core::Worker
|
4
|
+
include Arsenicum::Core::Commands
|
5
|
+
include Arsenicum::Core::IOHelper
|
6
|
+
|
7
|
+
attr_reader :pid, :in_parent, :out_parent,
|
8
|
+
:in_child, :out_child, :active, :broker, :serializer, :formatter
|
9
|
+
alias_method :active?, :active
|
10
|
+
|
11
|
+
def initialize(broker, worker_configuration)
|
12
|
+
@broker = WeakRef.new broker # avoiding circular references.
|
13
|
+
@serializer = worker_configuration[:serializer]
|
14
|
+
@formatter = worker_configuration[:formatter]
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
(@in_parent, @out_child) = IO.pipe
|
19
|
+
(@in_child, @out_parent) = IO.pipe
|
20
|
+
|
21
|
+
@pid = fork do
|
22
|
+
[in_parent, out_parent].each(&:close)
|
23
|
+
|
24
|
+
begin
|
25
|
+
loop do
|
26
|
+
begin
|
27
|
+
command = read_code in_child
|
28
|
+
break if command == COMMAND_STOP
|
29
|
+
task_id_string = read_string in_child
|
30
|
+
content = read_string in_child
|
31
|
+
rescue Arsenicum::IO::EOFException
|
32
|
+
# Interrupted request: No required GC.
|
33
|
+
break
|
34
|
+
end
|
35
|
+
|
36
|
+
task_id = task_id_string.to_sym
|
37
|
+
task = broker[task_id]
|
38
|
+
|
39
|
+
parameters = deserialize content
|
40
|
+
|
41
|
+
begin
|
42
|
+
task.run *parameters
|
43
|
+
write_code out_child, 0
|
44
|
+
rescue Exception => e
|
45
|
+
write_code out_child, 1
|
46
|
+
write_string out_child, Marshal.dump(e)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
ensure
|
50
|
+
[in_child, out_child].each do |io|
|
51
|
+
begin io.close rescue nil end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@active = true
|
56
|
+
[in_child, out_child].each(&:close)
|
57
|
+
pid
|
58
|
+
end
|
59
|
+
|
60
|
+
def ask(task_id, *parameter)
|
61
|
+
write_code out_parent, COMMAND_TASK
|
62
|
+
write_string out_parent, task_id.to_s
|
63
|
+
write_string out_parent, serialize(parameter)
|
64
|
+
|
65
|
+
result = read_code in_parent
|
66
|
+
return if result == 0
|
67
|
+
raise Marshal.restore(read_string in_parent, encoding: 'BINARY')
|
68
|
+
end
|
69
|
+
|
70
|
+
def terminate
|
71
|
+
write_code out_parent, COMMAND_STOP
|
72
|
+
Process.waitpid pid
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def serialize(parameter)
|
77
|
+
serializer.serialize(formatter.format(parameter))
|
78
|
+
end
|
79
|
+
|
80
|
+
def deserialize(string)
|
81
|
+
formatter.parse(serializer.deserialize(string))
|
82
|
+
end
|
83
|
+
|
84
|
+
def trap_signal
|
85
|
+
%w(TERM INT).each do |sig|
|
86
|
+
Signal.trap sig do
|
87
|
+
exit 5
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
class Arsenicum::Formatter
|
5
|
+
DATE_FORMAT = '%Y-%m-%d'.freeze
|
6
|
+
DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S %Z %z'.freeze
|
7
|
+
|
8
|
+
include Arsenicum::Util
|
9
|
+
|
10
|
+
def format(value)
|
11
|
+
format_for_embedded_classes(value) ||
|
12
|
+
format_by_extension(value) ||
|
13
|
+
format_by_default(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
TYPE_RAW = 'raw'.freeze
|
17
|
+
TYPE_DATE = 'date'.freeze
|
18
|
+
TYPE_DATETIME = 'datetime'.freeze
|
19
|
+
TYPE_CLASS = 'class'.freeze
|
20
|
+
TYPE_ARRAY = 'array'.freeze
|
21
|
+
TYPE_HASH = 'hash'.freeze
|
22
|
+
TYPE_ANY = 'marshal'.freeze
|
23
|
+
|
24
|
+
def format_for_embedded_classes(value)
|
25
|
+
case value
|
26
|
+
when Integer, Float, String, TrueClass, FalseClass, NilClass, Symbol
|
27
|
+
{
|
28
|
+
type: TYPE_RAW,
|
29
|
+
value: value.inspect,
|
30
|
+
}
|
31
|
+
when DateTime, Time
|
32
|
+
{
|
33
|
+
type: TYPE_DATETIME,
|
34
|
+
value: value.strftime(DATE_TIME_FORMAT),
|
35
|
+
}
|
36
|
+
when Date
|
37
|
+
{
|
38
|
+
type: TYPE_DATE,
|
39
|
+
value: value.strftime(DATE_FORMAT),
|
40
|
+
}
|
41
|
+
when Class
|
42
|
+
{
|
43
|
+
type: TYPE_CLASS,
|
44
|
+
value: value.name,
|
45
|
+
}
|
46
|
+
when Array
|
47
|
+
{
|
48
|
+
type: TYPE_ARRAY,
|
49
|
+
values: value.map{|v|format(v)},
|
50
|
+
}
|
51
|
+
when Hash
|
52
|
+
{
|
53
|
+
type: TYPE_HASH,
|
54
|
+
values: value.inject({}){|h, kv|(k,v)=kv;h[k.to_s]=format(v);h},
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def format_by_extension(value)
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_by_default(value)
|
64
|
+
{
|
65
|
+
type: TYPE_ANY,
|
66
|
+
value: Marshal.dump(value).unpack('H*').first,
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse(value)
|
71
|
+
value = normalize_hash(value)
|
72
|
+
|
73
|
+
return eval value[:value] if value[:type] == TYPE_RAW
|
74
|
+
parse_for_embedded_classes(value) ||
|
75
|
+
parse_by_extension(value) ||
|
76
|
+
parse_by_default(value)
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_for_embedded_classes(value)
|
80
|
+
case value[:type]
|
81
|
+
when TYPE_DATE
|
82
|
+
Date.strptime(value[:value], DATE_FORMAT)
|
83
|
+
when TYPE_DATETIME
|
84
|
+
Time.strptime(value[:value], DATE_TIME_FORMAT)
|
85
|
+
when TYPE_CLASS
|
86
|
+
Module.const_get value[:value].to_sym
|
87
|
+
when TYPE_ARRAY
|
88
|
+
value[:values].map do |value|
|
89
|
+
parse(value)
|
90
|
+
end
|
91
|
+
when TYPE_HASH
|
92
|
+
value[:values].inject({}) do |h, key_value|
|
93
|
+
(key, value) = key_value
|
94
|
+
h[key.to_s.to_sym] = parse(value)
|
95
|
+
h
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_by_extension(_)
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_by_default(value)
|
105
|
+
::Marshal.restore [value[:value]].pack('H*')
|
106
|
+
end
|
107
|
+
|
108
|
+
class ActiveRecord < ::Arsenicum::Formatter
|
109
|
+
TYPE_ACTIVE_RECORD = 'active_record'.freeze
|
110
|
+
|
111
|
+
def format_by_extension(value)
|
112
|
+
return {
|
113
|
+
type: TYPE_ACTIVE_RECORD,
|
114
|
+
class: value.class.name,
|
115
|
+
id: value.id,
|
116
|
+
} if value.is_a? ActiveRecord::Base
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_by_extension(value)
|
120
|
+
if value[:type] == TYPE_ACTIVE_RECORD
|
121
|
+
klass = constaitize value[:class].to_sym
|
122
|
+
klass.find value[:id]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
data/lib/arsenicum/io.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arsenicum
|
2
|
+
module Main
|
3
|
+
def run(config_file)
|
4
|
+
config = Arsenicum::Configuration.new
|
5
|
+
config_file = File.expand_path config_file
|
6
|
+
|
7
|
+
script = File.read config_file
|
8
|
+
config.instance_eval script, config_file, 1
|
9
|
+
|
10
|
+
File.open(config.pidfile_path, 'w:UTF-8') do |f|
|
11
|
+
f.puts $$
|
12
|
+
end
|
13
|
+
threads = config.queue_configurations.map{|qc|qc.build.start_async}
|
14
|
+
|
15
|
+
begin
|
16
|
+
threads.each(&:join)
|
17
|
+
rescue Interrupt
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :run
|
22
|
+
end
|
23
|
+
end
|
data/lib/arsenicum/task.rb
CHANGED
@@ -1,53 +1,13 @@
|
|
1
|
-
|
1
|
+
class Arsenicum::Task
|
2
|
+
attr_reader :id
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
attr_reader :target, :method, :arguments, :timestamp, :message_id, :exception
|
8
|
-
|
9
|
-
def self.parse(raw_message, message_id)
|
10
|
-
message_content = JSON(raw_message)
|
11
|
-
|
12
|
-
timestamp = message_content['timestamp']
|
13
|
-
method = message_content['method_name'].to_sym
|
14
|
-
|
15
|
-
target = restore(message_content['target'])
|
16
|
-
arguments = message_content['arguments'].nil? ? [] :
|
17
|
-
message_content['arguments'].map{|arg|restore(arg)}
|
18
|
-
|
19
|
-
new(target, method, arguments, timestamp, message_id)
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize(target, method, arguments, timestamp, message_id)
|
23
|
-
@target = target
|
24
|
-
@method = method
|
25
|
-
@arguments = arguments
|
26
|
-
@timestamp = timestamp
|
27
|
-
@message_id = message_id
|
28
|
-
end
|
29
|
-
|
30
|
-
def prepare_serialization
|
31
|
-
{
|
32
|
-
target: prepare_serialization(target),
|
33
|
-
timestamp: (Time.now.to_f * 1000000).to_i,
|
34
|
-
method_name: method_name,
|
35
|
-
arguments: arguments.nil? ? nil : arguments.map{|arg|prepare_serialization(arg)},
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
|
-
def serialize
|
40
|
-
JSON(prepare_serialization)
|
41
|
-
end
|
42
|
-
|
43
|
-
def execute!
|
44
|
-
target.__send__ method, *arguments
|
45
|
-
rescue Exception => e
|
46
|
-
@exception = e
|
47
|
-
end
|
4
|
+
def initialize(id)
|
5
|
+
@id = id
|
6
|
+
end
|
48
7
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
8
|
+
def run(*parameters)
|
9
|
+
# Originally do nothing. This will be overridden in the derived classes.
|
52
10
|
end
|
53
|
-
|
11
|
+
|
12
|
+
autoload :ClassDispatcher, 'arsenicum/task/class_dispatcher'
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Arsenicum::Task::ClassDispatcher < Arsenicum::Task
|
2
|
+
attr_reader :target_class, :target_method
|
3
|
+
private :target_class, :target_method
|
4
|
+
|
5
|
+
def initialize(id, options)
|
6
|
+
super(id)
|
7
|
+
@target_class = options.delete :type
|
8
|
+
@target_method = options.delete :target
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(*parameters)
|
12
|
+
target_class.new.__send__ target_method, *parameters
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Arsenicum
|
2
|
+
module Util
|
3
|
+
def self.included(base)
|
4
|
+
mod = self
|
5
|
+
base.module_eval{extend mod}
|
6
|
+
end
|
7
|
+
|
8
|
+
def normalize_hash(values)
|
9
|
+
values.inject({}) do |h, kv|
|
10
|
+
(key, value) = kv
|
11
|
+
value = normalize_hash(value) if value.is_a? Hash
|
12
|
+
h.tap{|i|i.merge!(key.to_sym => value)}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def camelcase(stringlike, upcase_first = true)
|
17
|
+
stringlike.to_s.dup.tap do |s|
|
18
|
+
s.gsub!(/_([a-z])/){$1.upcase}
|
19
|
+
s.gsub!(/^([a-z])/){$1.upcase} if upcase_first
|
20
|
+
end.to_sym
|
21
|
+
end
|
22
|
+
|
23
|
+
def classify(stringlike)
|
24
|
+
stringlike.to_s.split(/\/+/).map do |s|
|
25
|
+
camelcase(s)
|
26
|
+
end.join('::')
|
27
|
+
end
|
28
|
+
|
29
|
+
def underscore(stringlike)
|
30
|
+
stringlike.to_s.dup.tap do |s|
|
31
|
+
s.gsub!(/^([A-Z])/){$1.tap(&:downcase!)}
|
32
|
+
s.gsub!(/([A-Z])/){'_' << $1.tap(&:downcase!)}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def constantize(klass, inside: Kernel)
|
37
|
+
if klass.to_s.start_with?('::')
|
38
|
+
klass = klass.to_s[2..-1].to_sym
|
39
|
+
inside = Kernel
|
40
|
+
end
|
41
|
+
klass.to_s.split('::').inject(inside) do |parent, const|
|
42
|
+
parent.const_get const.to_sym
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
extend self
|
47
|
+
end
|
48
|
+
end
|