asynchronic 0.1.0 → 0.2.0
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.
- data/lib/asynchronic.rb +0 -2
- data/lib/asynchronic/data_store/helper.rb +42 -0
- data/lib/asynchronic/data_store/in_memory.rb +18 -22
- data/lib/asynchronic/data_store/key.rb +19 -1
- data/lib/asynchronic/data_store/lazy_store.rb +17 -0
- data/lib/asynchronic/data_store/lazy_value.rb +34 -0
- data/lib/asynchronic/data_store/readonly_store.rb +17 -0
- data/lib/asynchronic/data_store/redis.rb +16 -27
- data/lib/asynchronic/data_store/scoped_store.rb +52 -0
- data/lib/asynchronic/environment.rb +7 -27
- data/lib/asynchronic/job.rb +15 -27
- data/lib/asynchronic/process.rb +105 -76
- data/lib/asynchronic/queue_engine/in_memory.rb +5 -1
- data/lib/asynchronic/queue_engine/ost.rb +5 -1
- data/lib/asynchronic/queue_engine/synchronic.rb +68 -0
- data/lib/asynchronic/transparent_proxy.rb +52 -0
- data/lib/asynchronic/version.rb +1 -1
- data/spec/data_store/data_store_examples.rb +48 -32
- data/spec/data_store/in_memory_spec.rb +5 -0
- data/spec/data_store/key_spec.rb +36 -12
- data/spec/data_store/lazy_value_examples.rb +38 -0
- data/spec/data_store/redis_spec.rb +17 -0
- data/spec/data_store/scoped_store_spec.rb +60 -0
- data/spec/expectations.rb +7 -7
- data/spec/facade_spec.rb +15 -13
- data/spec/jobs.rb +70 -49
- data/spec/minitest_helper.rb +11 -1
- data/spec/process/life_cycle_examples.rb +149 -135
- data/spec/queue_engine/synchronic_spec.rb +27 -0
- data/spec/transparent_proxy_spec.rb +36 -0
- data/spec/worker/worker_examples.rb +1 -1
- metadata +117 -79
- checksums.yaml +0 -7
- data/lib/asynchronic/data_store/lookup.rb +0 -27
- data/lib/asynchronic/hash.rb +0 -31
- data/lib/asynchronic/runtime.rb +0 -40
- data/spec/data_store/lookup_spec.rb +0 -92
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
require_relative './data_store_examples'
|
3
|
+
require_relative './lazy_value_examples'
|
3
4
|
|
4
5
|
describe Asynchronic::DataStore::InMemory do
|
5
6
|
|
@@ -7,4 +8,8 @@ describe Asynchronic::DataStore::InMemory do
|
|
7
8
|
|
8
9
|
include DataStoreExamples
|
9
10
|
|
11
|
+
describe 'LazyValue' do
|
12
|
+
include LazyValueExamples
|
13
|
+
end
|
14
|
+
|
10
15
|
end
|
data/spec/data_store/key_spec.rb
CHANGED
@@ -2,35 +2,59 @@ require 'minitest_helper'
|
|
2
2
|
|
3
3
|
describe Asynchronic::DataStore::Key do
|
4
4
|
|
5
|
+
Key = Asynchronic::DataStore::Key
|
6
|
+
|
5
7
|
it 'Return the namespace' do
|
6
|
-
key =
|
8
|
+
key = Key.new 'foo'
|
7
9
|
key.must_equal 'foo'
|
8
10
|
end
|
9
11
|
|
10
12
|
it 'Prepend the namespace' do
|
11
|
-
key =
|
12
|
-
key['bar'].must_equal 'foo
|
13
|
+
key = Key.new 'foo'
|
14
|
+
key['bar'].must_equal 'foo|bar'
|
13
15
|
end
|
14
16
|
|
15
17
|
it 'Work in more than one level' do
|
16
|
-
key_1 =
|
17
|
-
key_2 =
|
18
|
-
key_2['baz'].must_equal 'foo
|
18
|
+
key_1 = Key.new 'foo'
|
19
|
+
key_2 = Key.new key_1['bar']
|
20
|
+
key_2['baz'].must_equal 'foo|bar|baz'
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'Be chainable' do
|
22
|
-
key =
|
23
|
-
key['bar']['baz'].must_equal 'foo
|
24
|
+
key = Key.new 'foo'
|
25
|
+
key['bar']['baz'].must_equal 'foo|bar|baz'
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'Accept symbols' do
|
27
|
-
key =
|
28
|
-
key[:bar].must_equal 'foo
|
29
|
+
key = Key.new :foo
|
30
|
+
key[:bar].must_equal 'foo|bar'
|
29
31
|
end
|
30
32
|
|
31
33
|
it 'Accept numbers' do
|
32
|
-
key =
|
33
|
-
key[3].must_equal 'foo
|
34
|
+
key = Key.new 'foo'
|
35
|
+
key[3].must_equal 'foo|3'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'Split in sections' do
|
39
|
+
key = Key.new(:foo)[:bar][:buz]
|
40
|
+
key.sections.must_equal %w(foo bar buz)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'Detect nested sections' do
|
44
|
+
Key.new(:foo).wont_be :nested?
|
45
|
+
Key.new(:foo)[:bar].must_be :nested?
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'Remove first sections' do
|
49
|
+
key = Key.new(:foo)[:bar][:buz]
|
50
|
+
key.remove_first.must_equal 'bar|buz'
|
51
|
+
key.remove_first(2).must_equal 'buz'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'Remove last sections' do
|
55
|
+
key = Key.new(:foo)[:bar][:buz]
|
56
|
+
key.remove_last.must_equal 'foo|bar'
|
57
|
+
key.remove_last(2).must_equal 'foo'
|
34
58
|
end
|
35
59
|
|
36
60
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module LazyValueExamples
|
2
|
+
|
3
|
+
def lazy_value(key)
|
4
|
+
Asynchronic::DataStore::LazyValue.new data_store, key
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'Get' do
|
8
|
+
value = lazy_value :key
|
9
|
+
value.must_be_nil
|
10
|
+
|
11
|
+
data_store[:key] = 1
|
12
|
+
value.must_equal 1
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'Reload' do
|
16
|
+
value = lazy_value :key
|
17
|
+
|
18
|
+
data_store[:key] = 1
|
19
|
+
value.must_equal 1
|
20
|
+
|
21
|
+
data_store[:key] = 2
|
22
|
+
value.must_equal 1
|
23
|
+
value.reload.must_equal 2
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'Transparent proxy' do
|
27
|
+
value = lazy_value :key
|
28
|
+
data_store[:key] = 1
|
29
|
+
value.must_be_instance_of Fixnum
|
30
|
+
value.must_equal 1
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Inspect' do
|
34
|
+
value = lazy_value :key
|
35
|
+
value.inspect.must_match /#<Asynchronic::DataStore::LazyValue @data_store_class=.+ @data_store_connection=.+ @key=key>/
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
require_relative './data_store_examples'
|
3
|
+
require_relative './lazy_value_examples'
|
3
4
|
|
4
5
|
describe Asynchronic::DataStore::Redis do
|
5
6
|
|
@@ -11,4 +12,20 @@ describe Asynchronic::DataStore::Redis do
|
|
11
12
|
|
12
13
|
include DataStoreExamples
|
13
14
|
|
15
|
+
it 'Safe deserialization' do
|
16
|
+
SampleClass = Class.new
|
17
|
+
|
18
|
+
data_store[:class] = SampleClass
|
19
|
+
data_store[:instance] = SampleClass.new
|
20
|
+
|
21
|
+
Object.send :remove_const, :SampleClass
|
22
|
+
|
23
|
+
data_store[:class].must_be_instance_of String
|
24
|
+
data_store[:instance].must_be_instance_of String
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'LazyValue' do
|
28
|
+
include LazyValueExamples
|
29
|
+
end
|
30
|
+
|
14
31
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Asynchronic::DataStore::ScopedStore do
|
4
|
+
|
5
|
+
let(:data_store) { MiniTest::Mock.new }
|
6
|
+
let(:scoped_store) { Asynchronic::DataStore::ScopedStore.new data_store, :scope }
|
7
|
+
|
8
|
+
it 'Get' do
|
9
|
+
data_store.expect(:[], 1, ['scope|key'])
|
10
|
+
scoped_store[:key].must_equal 1
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'Set' do
|
14
|
+
data_store.expect(:[]=, nil, ['scope|key', 1])
|
15
|
+
scoped_store[:key] = 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'Keys' do
|
19
|
+
data_store.expect(:keys, ['scope|a', 'scope|b'])
|
20
|
+
scoped_store.keys.must_equal ['a', 'b']
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'Delete' do
|
24
|
+
data_store.expect(:delete, nil, ['scope|key'])
|
25
|
+
scoped_store.delete :key
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'Each' do
|
29
|
+
data_store.expect(:keys, ['scope|a', 'scope|b'])
|
30
|
+
data_store.expect(:[], 1, ['scope|a'])
|
31
|
+
data_store.expect(:[], 2, ['scope|b'])
|
32
|
+
|
33
|
+
array = []
|
34
|
+
scoped_store.each { |k,v| array << "#{k} => #{v}" }
|
35
|
+
array.must_equal ['a => 1', 'b => 2']
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'Merge' do
|
39
|
+
data_store.expect(:[]=, nil, ['scope|a', 1])
|
40
|
+
data_store.expect(:[]=, nil, ['scope|b', 2])
|
41
|
+
scoped_store.merge a: 1, b: 2
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'Clear' do
|
45
|
+
data_store.expect(:keys, ['scope|key'])
|
46
|
+
data_store.expect(:delete, nil, ['scope|key'])
|
47
|
+
scoped_store.clear
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'Scoped' do
|
51
|
+
data_store.expect(:[], 1, ['scope|nested|key'])
|
52
|
+
nested = scoped_store.scoped :nested
|
53
|
+
nested[:key].must_equal 1
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'To string' do
|
57
|
+
scoped_store.to_s.must_match /#<Asynchronic::DataStore::ScopedStore @data_store=.+ @scope=scope>/
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/expectations.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module MiniTest::Assertions
|
2
2
|
|
3
3
|
def assert_enqueued(expected_processes, queue)
|
4
|
-
messages = Array(expected_processes).map
|
5
|
-
queue.to_a.sort.must_equal messages.sort, "
|
4
|
+
messages = Array(expected_processes).map(&:id)
|
5
|
+
queue.to_a.sort.must_equal messages.sort, "Processes #{Array(expected_processes).map(&:id)}"
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
process.
|
8
|
+
def assert_have_params(expected_hash, process)
|
9
|
+
process.params.keys.count.must_equal expected_hash.keys.count, "Missing keys\nExpected keys: #{expected_hash.keys.map(&:to_s)}\n Actual keys: #{process.params.keys}"
|
10
10
|
expected_hash.each do |k,v|
|
11
|
-
process[k]
|
11
|
+
assert process.params[k] == v, "Different key '#{k}'\n Actual: #{process.params[k]}\nExpected: #{v}"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -17,7 +17,7 @@ module MiniTest::Assertions
|
|
17
17
|
process.wont_be :finalized?
|
18
18
|
|
19
19
|
process.processes.must_be_empty
|
20
|
-
process.data.must_be_empty
|
20
|
+
# process.data.must_be_empty
|
21
21
|
process.error.must_be_nil
|
22
22
|
|
23
23
|
process.created_at.must_be_instance_of Time
|
@@ -80,7 +80,7 @@ end
|
|
80
80
|
|
81
81
|
Asynchronic::QueueEngine::InMemory::Queue.infect_an_assertion :assert_enqueued, :must_enqueued
|
82
82
|
Asynchronic::QueueEngine::Ost::Queue.infect_an_assertion :assert_enqueued, :must_enqueued
|
83
|
-
Asynchronic::Process.infect_an_assertion :
|
83
|
+
Asynchronic::Process.infect_an_assertion :assert_have_params, :must_have_params
|
84
84
|
Asynchronic::Process.infect_an_assertion :assert_be_initialized, :must_be_initialized, :unary
|
85
85
|
Asynchronic::Process.infect_an_assertion :assert_be_pending, :must_be_pending, :unary
|
86
86
|
Asynchronic::Process.infect_an_assertion :assert_be_queued, :must_be_queued, :unary
|
data/spec/facade_spec.rb
CHANGED
@@ -26,35 +26,37 @@ describe Asynchronic, 'Facade' do
|
|
26
26
|
it 'Environment' do
|
27
27
|
Asynchronic.environment.tap do |env|
|
28
28
|
env.queue_engine.must_equal Asynchronic.queue_engine
|
29
|
-
env.data_store.must_equal Asynchronic.data_store
|
29
|
+
env.data_store.connection.must_equal Asynchronic.data_store.scoped(:asynchronic).connection
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'Load process' do
|
34
|
-
process = Asynchronic.environment.
|
35
|
-
Asynchronic[process.
|
36
|
-
p.
|
37
|
-
p.
|
34
|
+
process = Asynchronic.environment.create_process BasicJob
|
35
|
+
Asynchronic[process.id].tap do |p|
|
36
|
+
p.id.must_equal process.id
|
37
|
+
p.type.must_equal process.type
|
38
|
+
p.created_at.must_equal process.created_at
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
it 'List processes' do
|
42
|
-
|
43
|
-
process = Asynchronic.environment.
|
44
|
-
process.
|
43
|
+
ids = 3.times.map do
|
44
|
+
process = Asynchronic.environment.create_process SequentialJob
|
45
|
+
process.id
|
45
46
|
end
|
46
47
|
|
47
48
|
Asynchronic.processes.count.must_equal 3
|
48
|
-
|
49
|
+
3.times { |i| Asynchronic.processes[i].id == ids[i] }
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'Enqueue' do
|
52
|
-
|
53
|
+
id = BasicJob.enqueue input: 100
|
53
54
|
|
54
55
|
Asynchronic.environment.tap do |env|
|
55
|
-
env.
|
56
|
-
|
57
|
-
|
56
|
+
process = env.load_process id
|
57
|
+
process.type.must_equal BasicJob
|
58
|
+
process.params[:input].must_equal 100
|
59
|
+
env.default_queue.must_enqueued process
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
data/spec/jobs.rb
CHANGED
@@ -1,26 +1,30 @@
|
|
1
1
|
class BasicJob < Asynchronic::Job
|
2
|
-
|
3
|
-
|
2
|
+
def call
|
3
|
+
params[:input] + 1
|
4
4
|
end
|
5
5
|
end
|
6
6
|
|
7
7
|
|
8
8
|
class SequentialJob < Asynchronic::Job
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def call
|
11
|
+
async Step1, input: params[:input]
|
12
|
+
|
13
|
+
async Step2, dependency: Step1,
|
14
|
+
input: params[:input]
|
15
|
+
|
16
|
+
nil
|
13
17
|
end
|
14
18
|
|
15
19
|
class Step1 < Asynchronic::Job
|
16
|
-
|
17
|
-
|
20
|
+
def call
|
21
|
+
params[:input] * 10
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
25
|
class Step2 < Asynchronic::Job
|
22
|
-
|
23
|
-
|
26
|
+
def call
|
27
|
+
params[:input] / 10
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
@@ -29,34 +33,40 @@ end
|
|
29
33
|
|
30
34
|
class GraphJob < Asynchronic::Job
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
def call
|
37
|
+
async Sum, input: params[:input]
|
38
|
+
|
39
|
+
async TenPercent, input: result(Sum)
|
40
|
+
|
41
|
+
async TwentyPercent, input: result(Sum)
|
42
|
+
|
43
|
+
async Total, '10%' => result(TenPercent),
|
44
|
+
'20%' => result(TwentyPercent)
|
45
|
+
|
46
|
+
result Total
|
37
47
|
end
|
38
48
|
|
39
49
|
class Sum < Asynchronic::Job
|
40
|
-
|
41
|
-
|
50
|
+
def call
|
51
|
+
params[:input] + 100
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
55
|
class TenPercent < Asynchronic::Job
|
46
|
-
|
47
|
-
|
56
|
+
def call
|
57
|
+
params[:input] * 0.1
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
51
61
|
class TwentyPercent < Asynchronic::Job
|
52
|
-
|
53
|
-
|
62
|
+
def call
|
63
|
+
params[:input] * 0.2
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
57
67
|
class Total < Asynchronic::Job
|
58
|
-
|
59
|
-
|
68
|
+
def call
|
69
|
+
{'10%' => params['10%'], '20%' => params['20%']}
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
@@ -64,50 +74,60 @@ end
|
|
64
74
|
|
65
75
|
|
66
76
|
class ParallelJob < Asynchronic::Job
|
67
|
-
|
68
|
-
|
69
|
-
|
77
|
+
def call
|
78
|
+
params[:times].times do |i|
|
79
|
+
async Child, input: params[:input], index: i
|
70
80
|
end
|
71
81
|
end
|
72
82
|
|
73
83
|
class Child < Asynchronic::Job
|
74
|
-
|
75
|
-
|
84
|
+
def call
|
85
|
+
params[:input] * params[:index]
|
76
86
|
end
|
77
87
|
end
|
78
88
|
end
|
79
89
|
|
80
90
|
|
81
91
|
class NestedJob < Asynchronic::Job
|
82
|
-
|
83
|
-
|
92
|
+
def call
|
93
|
+
async Level1, input: params[:input]
|
94
|
+
result Level1
|
84
95
|
end
|
85
96
|
|
86
97
|
class Level1 < Asynchronic::Job
|
87
|
-
|
88
|
-
|
89
|
-
|
98
|
+
def call
|
99
|
+
async Level2, input: params[:input] + 1
|
100
|
+
result Level2
|
90
101
|
end
|
91
102
|
|
92
103
|
class Level2 < Asynchronic::Job
|
93
|
-
|
94
|
-
|
104
|
+
def call
|
105
|
+
params[:input] ** 2
|
95
106
|
end
|
96
107
|
end
|
97
108
|
end
|
98
109
|
end
|
99
110
|
|
100
111
|
|
101
|
-
class
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
112
|
+
class AliasJob < Asynchronic::Job
|
113
|
+
def call
|
114
|
+
async Write, alias: :word_1,
|
115
|
+
text: 'Take'
|
116
|
+
|
117
|
+
async Write, alias: :word_2,
|
118
|
+
text: 'it',
|
119
|
+
prefix: result(:word_1)
|
120
|
+
|
121
|
+
async Write, alias: :word_3,
|
122
|
+
text: 'easy',
|
123
|
+
prefix: result(:word_2)
|
124
|
+
|
125
|
+
result :word_3
|
106
126
|
end
|
107
127
|
|
108
128
|
class Write < Asynchronic::Job
|
109
|
-
|
110
|
-
|
129
|
+
def call
|
130
|
+
[params[:prefix], params[:text]].compact.join(' ')
|
111
131
|
end
|
112
132
|
end
|
113
133
|
end
|
@@ -115,34 +135,35 @@ end
|
|
115
135
|
|
116
136
|
class CustomQueueJob < Asynchronic::Job
|
117
137
|
queue :queue_1
|
118
|
-
|
119
|
-
|
138
|
+
def call
|
139
|
+
async Reverse, queue: :queue_2, input: params[:input]
|
140
|
+
result Reverse
|
120
141
|
end
|
121
142
|
|
122
143
|
class Reverse < Asynchronic::Job
|
123
144
|
queue :queue_3
|
124
|
-
|
125
|
-
|
145
|
+
def call
|
146
|
+
params[:input].reverse
|
126
147
|
end
|
127
148
|
end
|
128
149
|
end
|
129
150
|
|
130
151
|
|
131
152
|
class ExceptionJob < Asynchronic::Job
|
132
|
-
|
153
|
+
def call
|
133
154
|
raise 'Error for test'
|
134
155
|
end
|
135
156
|
end
|
136
157
|
|
137
158
|
|
138
159
|
class InnerExceptionJob < Asynchronic::Job
|
139
|
-
|
140
|
-
|
160
|
+
def call
|
161
|
+
async ExceptionJob
|
141
162
|
end
|
142
163
|
end
|
143
164
|
|
144
165
|
|
145
166
|
class WorkerJob < Asynchronic::Job
|
146
|
-
|
167
|
+
def call
|
147
168
|
end
|
148
169
|
end
|