asynchronic 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/lib/asynchronic.rb +0 -2
  2. data/lib/asynchronic/data_store/helper.rb +42 -0
  3. data/lib/asynchronic/data_store/in_memory.rb +18 -22
  4. data/lib/asynchronic/data_store/key.rb +19 -1
  5. data/lib/asynchronic/data_store/lazy_store.rb +17 -0
  6. data/lib/asynchronic/data_store/lazy_value.rb +34 -0
  7. data/lib/asynchronic/data_store/readonly_store.rb +17 -0
  8. data/lib/asynchronic/data_store/redis.rb +16 -27
  9. data/lib/asynchronic/data_store/scoped_store.rb +52 -0
  10. data/lib/asynchronic/environment.rb +7 -27
  11. data/lib/asynchronic/job.rb +15 -27
  12. data/lib/asynchronic/process.rb +105 -76
  13. data/lib/asynchronic/queue_engine/in_memory.rb +5 -1
  14. data/lib/asynchronic/queue_engine/ost.rb +5 -1
  15. data/lib/asynchronic/queue_engine/synchronic.rb +68 -0
  16. data/lib/asynchronic/transparent_proxy.rb +52 -0
  17. data/lib/asynchronic/version.rb +1 -1
  18. data/spec/data_store/data_store_examples.rb +48 -32
  19. data/spec/data_store/in_memory_spec.rb +5 -0
  20. data/spec/data_store/key_spec.rb +36 -12
  21. data/spec/data_store/lazy_value_examples.rb +38 -0
  22. data/spec/data_store/redis_spec.rb +17 -0
  23. data/spec/data_store/scoped_store_spec.rb +60 -0
  24. data/spec/expectations.rb +7 -7
  25. data/spec/facade_spec.rb +15 -13
  26. data/spec/jobs.rb +70 -49
  27. data/spec/minitest_helper.rb +11 -1
  28. data/spec/process/life_cycle_examples.rb +149 -135
  29. data/spec/queue_engine/synchronic_spec.rb +27 -0
  30. data/spec/transparent_proxy_spec.rb +36 -0
  31. data/spec/worker/worker_examples.rb +1 -1
  32. metadata +117 -79
  33. checksums.yaml +0 -7
  34. data/lib/asynchronic/data_store/lookup.rb +0 -27
  35. data/lib/asynchronic/hash.rb +0 -31
  36. data/lib/asynchronic/runtime.rb +0 -40
  37. 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
@@ -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 = Asynchronic::DataStore::Key.new('foo')
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 = Asynchronic::DataStore::Key.new('foo')
12
- key['bar'].must_equal 'foo:bar'
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 = Asynchronic::DataStore::Key.new('foo')
17
- key_2 = Asynchronic::DataStore::Key.new(key_1['bar'])
18
- key_2['baz'].must_equal 'foo:bar:baz'
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 = Asynchronic::DataStore::Key.new('foo')
23
- key['bar']['baz'].must_equal 'foo:bar:baz'
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 = Asynchronic::DataStore::Key.new(:foo)
28
- key[:bar].must_equal 'foo:bar'
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 = Asynchronic::DataStore::Key.new('foo')
33
- key[3].must_equal 'foo:3'
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 { |p| p.job.lookup.id }
5
- queue.to_a.sort.must_equal messages.sort, "Jobs #{Array(expected_processes).map{ |p| p.job.name }}"
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 assert_have(expected_hash, process)
9
- process.data.keys.count.must_equal expected_hash.keys.count, "Missing keys\nExpected keys: #{expected_hash.keys}\n Actual keys: #{process.data.keys}"
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].must_equal v, "Key #{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 :assert_have, :must_have
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.build_process BasicJob
35
- Asynchronic[process.pid].tap do |p|
36
- p.pid.must_equal process.pid
37
- p.job.must_equal process.job
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
- pids = 3.times.map do
43
- process = Asynchronic.environment.build_process SequentialJob
44
- process.pid
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
- Asynchronic.processes.map(&:pid).each { |pid| pids.must_include pid }
49
+ 3.times { |i| Asynchronic.processes[i].id == ids[i] }
49
50
  end
50
51
 
51
52
  it 'Enqueue' do
52
- pid = BasicJob.enqueue input: 100
53
+ id = BasicJob.enqueue input: 100
53
54
 
54
55
  Asynchronic.environment.tap do |env|
55
- env.default_queue.to_a.must_equal [pid]
56
- env[pid].must_be_instance_of BasicJob
57
- env.load_process(pid)[:input].must_equal 100
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
- define do
3
- data[:output] = data[:input] + 1
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
- define do
11
- define_job Step1
12
- define_job Step2, dependency: Step1
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
- define do
17
- data[:partial] = data[:input] * 10
20
+ def call
21
+ params[:input] * 10
18
22
  end
19
23
  end
20
24
 
21
25
  class Step2 < Asynchronic::Job
22
- define do
23
- data[:output] = data[:partial] / 100
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
- define do
33
- define_job Sum
34
- define_job TenPercent, dependency: Sum
35
- define_job TwentyPercent, dependency: Sum
36
- define_job Total, dependencies: [TenPercent, TwentyPercent]
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
- define do
41
- data[:sum] = data[:input] + 100
50
+ def call
51
+ params[:input] + 100
42
52
  end
43
53
  end
44
54
 
45
55
  class TenPercent < Asynchronic::Job
46
- define do
47
- data['10%'] = data[:sum] * 0.1
56
+ def call
57
+ params[:input] * 0.1
48
58
  end
49
59
  end
50
60
 
51
61
  class TwentyPercent < Asynchronic::Job
52
- define do
53
- data['20%'] = data[:sum] * 0.2
62
+ def call
63
+ params[:input] * 0.2
54
64
  end
55
65
  end
56
66
 
57
67
  class Total < Asynchronic::Job
58
- define do
59
- data[:output] = {'10%' => data['10%'], '20%' => data['20%']}
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
- define do
68
- data[:times].times do |i|
69
- define_job Child, local: {index: i}
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
- define do
75
- data["key_#{index}"] = data[:input] * index
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
- define do
83
- define_job Level1
92
+ def call
93
+ async Level1, input: params[:input]
94
+ result Level1
84
95
  end
85
96
 
86
97
  class Level1 < Asynchronic::Job
87
- define do
88
- data[:input] += 1
89
- define_job Level2
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
- define do
94
- data[:output] = data[:input] ** 2
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 DependencyAliasJob < Asynchronic::Job
102
- define do
103
- define_job Write, local: {text: 'Take'}, alias: :word_1
104
- define_job Write, local: {text: 'it'}, alias: :word_2, dependency: :word_1
105
- define_job Write, local: {text: 'easy'}, alias: :word_3, dependency: :word_2
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
- define do
110
- data[:text] = "#{data[:text]} #{text}".strip
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
- define do
119
- define_job Reverse, queue: :queue_2
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
- define do
125
- data[:output] = data[:input].reverse
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
- define do
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
- define do
140
- define_job ExceptionJob
160
+ def call
161
+ async ExceptionJob
141
162
  end
142
163
  end
143
164
 
144
165
 
145
166
  class WorkerJob < Asynchronic::Job
146
- define do
167
+ def call
147
168
  end
148
169
  end