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.
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