reacto 0.0.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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +32 -0
  5. data/README.md +1 -0
  6. data/lib/reacto/behaviours.rb +50 -0
  7. data/lib/reacto/constants.rb +7 -0
  8. data/lib/reacto/executors.rb +31 -0
  9. data/lib/reacto/operations/buffer.rb +80 -0
  10. data/lib/reacto/operations/concat.rb +23 -0
  11. data/lib/reacto/operations/diff.rb +36 -0
  12. data/lib/reacto/operations/drop.rb +41 -0
  13. data/lib/reacto/operations/drop_errors.rb +15 -0
  14. data/lib/reacto/operations/flat_map.rb +26 -0
  15. data/lib/reacto/operations/flatten.rb +26 -0
  16. data/lib/reacto/operations/inject.rb +43 -0
  17. data/lib/reacto/operations/last.rb +31 -0
  18. data/lib/reacto/operations/map.rb +38 -0
  19. data/lib/reacto/operations/merge.rb +47 -0
  20. data/lib/reacto/operations/prepend.rb +19 -0
  21. data/lib/reacto/operations/select.rb +25 -0
  22. data/lib/reacto/operations/take.rb +38 -0
  23. data/lib/reacto/operations/throttle.rb +60 -0
  24. data/lib/reacto/operations/track_on.rb +18 -0
  25. data/lib/reacto/operations/uniq.rb +22 -0
  26. data/lib/reacto/operations.rb +23 -0
  27. data/lib/reacto/resources/executor_resource.rb +19 -0
  28. data/lib/reacto/resources.rb +1 -0
  29. data/lib/reacto/subscriptions/buffered_subscription.rb +52 -0
  30. data/lib/reacto/subscriptions/combining_last_subscription.rb +22 -0
  31. data/lib/reacto/subscriptions/combining_subscription.rb +8 -0
  32. data/lib/reacto/subscriptions/composite_subscription.rb +82 -0
  33. data/lib/reacto/subscriptions/executor_subscription.rb +63 -0
  34. data/lib/reacto/subscriptions/flat_map_subscription.rb +20 -0
  35. data/lib/reacto/subscriptions/inner_subscription.rb +47 -0
  36. data/lib/reacto/subscriptions/operation_subscription.rb +25 -0
  37. data/lib/reacto/subscriptions/simple_subscription.rb +80 -0
  38. data/lib/reacto/subscriptions/subscription.rb +21 -0
  39. data/lib/reacto/subscriptions/subscription_wrapper.rb +16 -0
  40. data/lib/reacto/subscriptions/tracker_subscription.rb +76 -0
  41. data/lib/reacto/subscriptions/zipping_subscription.rb +43 -0
  42. data/lib/reacto/subscriptions.rb +27 -0
  43. data/lib/reacto/trackable.rb +317 -0
  44. data/lib/reacto/tracker.rb +33 -0
  45. data/lib/reacto/version.rb +4 -0
  46. data/lib/reacto.rb +10 -0
  47. data/reacto.gemspec +27 -0
  48. data/spec/reacto/create_trackable_spec.rb +245 -0
  49. data/spec/reacto/executors_and_trackable_spec.rb +32 -0
  50. data/spec/reacto/operations/track_on_spec.rb +20 -0
  51. data/spec/reacto/trackable/buffer_spec.rb +58 -0
  52. data/spec/reacto/trackable/throttle_spec.rb +15 -0
  53. data/spec/reacto/trackable/zip_spec.rb +25 -0
  54. data/spec/reacto/trackable_spec.rb +417 -0
  55. data/spec/spec_helper.rb +15 -0
  56. data/spec/support/helpers.rb +16 -0
  57. metadata +150 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 00345a7d238aab953495d04d30b70de59a43b452
4
+ data.tar.gz: bf82a8089e0b732e456c14a8100d8f8ecf260370
5
+ SHA512:
6
+ metadata.gz: 552be561f5bec4bc6ab3b75f1f814b5d326dbae95328e83334d17811fcc0a23a6259ae946905fed819700a62d2b61d7119d2f378eddc479bc2518def7308194b
7
+ data.tar.gz: 972a2db20596802f0957760eadf2b58901bcd20d08211f9332ef633fd1f707833a1ca4f0bc4a07b151c0e3bc1bdf4baea8c5b1e813500c8556a01f7541497766
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ reacto (0.0.0)
5
+ concurrent-ruby (~> 1.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ concurrent-ruby (1.0.0)
11
+ diff-lcs (1.2.5)
12
+ rspec (3.3.0)
13
+ rspec-core (~> 3.3.0)
14
+ rspec-expectations (~> 3.3.0)
15
+ rspec-mocks (~> 3.3.0)
16
+ rspec-core (3.3.2)
17
+ rspec-support (~> 3.3.0)
18
+ rspec-expectations (3.3.1)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.3.0)
21
+ rspec-mocks (3.3.2)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.3.0)
24
+ rspec-support (3.3.0)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bundler (~> 1.3)
31
+ reacto!
32
+ rspec (~> 3.3.0)
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # reacto
@@ -0,0 +1,50 @@
1
+ module Reacto
2
+ module Behaviours
3
+ module_function
4
+
5
+ def with_close_and_error(tracker, &block)
6
+ begin
7
+ yield tracker if block_given?
8
+ tracker.on_close if tracker.subscribed?
9
+ rescue StandardError => error
10
+ tracker.on_error(error) if tracker.subscribed?
11
+ end
12
+ end
13
+
14
+ def single_tracker_value(tracker, value)
15
+ with_close_and_error(tracker) do |subscriber|
16
+ subscriber.on_value(value) if subscriber.subscribed?
17
+ end
18
+ end
19
+
20
+ def single_value(value)
21
+ lambda do |tracker|
22
+ with_close_and_error(tracker) do |subscriber|
23
+ subscriber.on_value(value) if subscriber.subscribed?
24
+ end
25
+ end
26
+ end
27
+
28
+ def integers_enumerator
29
+ Enumerator.new do |yielder|
30
+ n = 0
31
+ loop do
32
+ yielder.yield n
33
+ n = n + 1
34
+ end
35
+ end
36
+ end
37
+
38
+ def array_repeat_enumerator(array)
39
+ size = array.size
40
+
41
+ Enumerator.new do |yielder|
42
+ n = 0
43
+ loop do
44
+ yielder.yield array[n % size]
45
+ n = n + 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ module Reacto
2
+ NOTHING = {}
3
+ NO_ACTION = -> (*args) {}
4
+ DEFAULT_ON_ERROR = -> (e) { raise e }
5
+ ID = -> (v) { v }
6
+ NO_VALUE = {}
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'concurrent/executor/immediate_executor'
2
+ require 'concurrent/executor/cached_thread_pool'
3
+ require 'concurrent/executor/fixed_thread_pool'
4
+
5
+ module Reacto
6
+ module Executors
7
+ class CurrentExecutor
8
+ def post
9
+
10
+ end
11
+ end
12
+
13
+ module_function
14
+
15
+ def immediate
16
+ Concurrent::ImmediateExecutor.new
17
+ end
18
+
19
+ def current
20
+ Concurrent::ImmediateExecutor.new
21
+ end
22
+
23
+ def io
24
+ Concurrent::CachedThreadPool.new
25
+ end
26
+
27
+ def tasks
28
+ Concurrent::FixedThreadPool.new(4) # Number of cores here?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,80 @@
1
+ require 'concurrent'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Buffer
7
+ def initialize(count: nil, delay: nil)
8
+ @count = count
9
+ @delay = delay
10
+ @buffer = []
11
+ end
12
+
13
+ def call(tracker)
14
+ close = lambda do
15
+ finish(tracker)
16
+ tracker.on_close
17
+ end
18
+ error = lambda do |e|
19
+ finish(tracker)
20
+ tracker.on_error(e)
21
+ end
22
+ value = if !@count.nil? && @delay.nil?
23
+ count_buffer_behaviour(tracker)
24
+ elsif @count.nil? && !@delay.nil?
25
+ delay_buffer_behaviour(tracker)
26
+ elsif @count && @delay
27
+ count_and_delay_buffer_behaviour(tracker)
28
+ else
29
+ tracker.method(:on_value)
30
+ end
31
+
32
+ Subscriptions::OperationSubscription.new(
33
+ tracker,
34
+ value: value,
35
+ close: close,
36
+ error: error
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ def finish(tracker)
43
+ @task.shutdown if @task
44
+
45
+ tracker.on_value(@buffer) unless @buffer.empty?
46
+ end
47
+
48
+ def count_buffer_behaviour(tracker)
49
+ lambda do |value|
50
+ @buffer << value
51
+
52
+ if @buffer.size >= @count
53
+ tracker.on_value(@buffer)
54
+ @buffer = []
55
+ end
56
+ end
57
+ end
58
+
59
+ def delay_task(tracker)
60
+ @task = Concurrent::TimerTask.new(execution_interval: @delay) do
61
+ unless @buffer.empty?
62
+ tracker.on_value(@buffer)
63
+ @buffer = []
64
+ end
65
+ end
66
+ @task.execute
67
+ end
68
+
69
+ def delay_buffer_behaviour(tracker)
70
+ delay_task(tracker)
71
+ -> (value) { @buffer << value }
72
+ end
73
+
74
+ def count_and_delay_buffer_behaviour(tracker)
75
+ delay_task(tracker)
76
+ count_buffer_behaviour(tracker)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Concat
6
+ # TODO Continue on error flag!
7
+ def initialize(trackable)
8
+ @trackable = trackable
9
+ end
10
+
11
+ def call(tracker)
12
+ Subscriptions::OperationSubscription.new(
13
+ tracker,
14
+ close: -> () { @trackable.send(:do_track, tracker) },
15
+ error: -> (e) {
16
+ tracker.on_error(e)
17
+ @trackable.send(:do_track, tracker)
18
+ }
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Diff
7
+ DEFAULT_FN = -> (a, b) { [a, b] }
8
+
9
+ def initialize(fn = DEFAULT_FN, initial = NO_VALUE)
10
+ @fn = fn
11
+ @prev = initial
12
+ end
13
+
14
+ def call(tracker)
15
+ value = lambda do |v|
16
+ if @prev == NO_VALUE
17
+ @prev = v
18
+ return
19
+ end
20
+
21
+ current = @fn.call(@prev, v)
22
+ @prev = v
23
+
24
+ tracker.on_value(current)
25
+ end
26
+
27
+
28
+ Subscriptions::OperationSubscription.new(
29
+ tracker,
30
+ value: value
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,41 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Drop
7
+ def initialize(how_many_to_drop, offset = NO_VALUE)
8
+ if how_many_to_drop < 0
9
+ raise ArgumentError.new('Attempt to drop negative size!')
10
+ end
11
+
12
+ @how_many_to_drop = how_many_to_drop
13
+ @dropped = 0
14
+ @offset = offset
15
+ end
16
+
17
+ def call(tracker)
18
+ behaviour = lambda do |value|
19
+ @dropped += 1
20
+
21
+ if @dropped > @how_many_to_drop
22
+ if @offset != NO_VALUE
23
+ if @offset <= 0
24
+ tracker.on_close
25
+ return
26
+ else
27
+ @offset -= 1
28
+ end
29
+ end
30
+ tracker.on_value(value)
31
+ end
32
+ end
33
+
34
+ Subscriptions::OperationSubscription.new(
35
+ tracker,
36
+ value: behaviour
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class DropErrors
6
+ def call(tracker)
7
+ Subscriptions::OperationSubscription.new(
8
+ tracker,
9
+ error: ->(e) {}
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,26 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+ require 'reacto/subscriptions/flat_map_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class FlatMap
7
+ def initialize(transform)
8
+ @transform = transform
9
+ end
10
+
11
+ def call(tracker)
12
+ subscription = Subscriptions::FlatMapSubscription.new(tracker)
13
+ value = lambda do |v|
14
+ trackable = @transform.call(v)
15
+
16
+ trackable.do_track subscription.subscription!
17
+ end
18
+
19
+ Subscriptions::OperationSubscription.new(
20
+ tracker, value: value
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,26 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Flatten
6
+
7
+ def call(tracker)
8
+ behaviour = lambda do |value|
9
+ if value.kind_of?(Array)
10
+ value.flatten.each do |sub_value|
11
+ tracker.on_value(sub_value)
12
+ end
13
+ else
14
+ tracker.on_value(value)
15
+ end
16
+ end
17
+
18
+ Subscriptions::OperationSubscription.new(
19
+ tracker,
20
+ value: behaviour
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,43 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Inject
7
+
8
+ def initialize(injector, initial = NO_VALUE)
9
+ @injector = injector
10
+ @current = initial
11
+ @has_values = false
12
+ end
13
+
14
+ def call(tracker)
15
+ inject = lambda do |v|
16
+ if @current == NO_VALUE
17
+ @current = v
18
+ else
19
+ @current = @injector.call(@current, v)
20
+ end
21
+
22
+ @has_values = true
23
+ tracker.on_value(@current)
24
+ end
25
+
26
+ close = lambda do
27
+ unless @has_values || @current == NO_VALUE
28
+ tracker.on_value(@current)
29
+ end
30
+
31
+ tracker.on_close
32
+ end
33
+
34
+ Subscriptions::OperationSubscription.new(
35
+ tracker,
36
+ value: inject,
37
+ close: close
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,31 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Last
6
+ def initialize
7
+ @marker = {}
8
+ @prev = @marker
9
+ end
10
+
11
+ def call(tracker)
12
+ close = lambda do
13
+ tracker.on_value(@prev) if @prev != @marker
14
+ tracker.on_close
15
+ end
16
+ error = lambda do |e|
17
+ tracker.on_value(@prev) if @prev != @marker
18
+ tracker.on_error(e)
19
+ end
20
+
21
+ Subscriptions::OperationSubscription.new(
22
+ tracker,
23
+ value: -> (v) { @prev = v },
24
+ error: error,
25
+ close: close
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,38 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Map
7
+ def initialize(mapping, error: nil, close: nil)
8
+ @mapping = mapping
9
+ @error = error
10
+ @close = close
11
+ end
12
+
13
+ def call(tracker)
14
+ error = if @error
15
+ -> (e) { tracker.on_error(@error.call(e)) }
16
+ else
17
+ tracker.method(:on_error)
18
+ end
19
+
20
+ close = if @close
21
+ -> () do
22
+ tracker.on_value(@close.call)
23
+ tracker.on_close
24
+ end
25
+ else
26
+ tracker.method(:on_close)
27
+ end
28
+
29
+ Subscriptions::OperationSubscription.new(
30
+ tracker,
31
+ value: -> (v) { tracker.on_value(@mapping.call(v)) },
32
+ error: error,
33
+ close: close
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Merge
6
+ def initialize(trackable, delay_error: false)
7
+ @trackable = trackable
8
+ @close_notifications = 2
9
+ @lock = Mutex.new
10
+ @delay_error = delay_error
11
+ end
12
+
13
+ def call(tracker)
14
+ close = lambda do
15
+ @lock.synchronize do
16
+ @close_notifications -= 1
17
+ if @close_notifications == 0
18
+ @error.nil? ? tracker.on_close : tracker.on_error(@error)
19
+ end
20
+ end
21
+ end
22
+ err =
23
+ if @delay_error
24
+ lambda do |er|
25
+ @lock.synchronize do
26
+ @error = er
27
+ @close_notifications -= 1
28
+ tracker.on_error(@error) if @close_notifications == 0
29
+ end
30
+ end
31
+ else
32
+ tracker.method(:on_error)
33
+ end
34
+
35
+ sub = Subscriptions::OperationSubscription.new(
36
+ tracker,
37
+ close: close,
38
+ error: err
39
+ )
40
+
41
+ @trackable.send(:do_track, sub)
42
+ sub
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,19 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Prepend
6
+ attr_reader :enumerable
7
+
8
+ def initialize(enumerable)
9
+ @enumerable = enumerable
10
+ end
11
+
12
+ def call(tracker)
13
+ enumerable.each { |value| tracker.on_value(value) }
14
+ tracker
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,25 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Select
6
+ def initialize(filter)
7
+ @filter = filter
8
+ end
9
+
10
+ def call(tracker)
11
+ on_value = lambda do |v|
12
+ if @filter.call(v)
13
+ tracker.on_value(v)
14
+ end
15
+ end
16
+
17
+ Subscriptions::OperationSubscription.new(
18
+ tracker,
19
+ value: on_value
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,38 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Take
6
+ def initialize(how_many_to_take)
7
+ if how_many_to_take < 0
8
+ raise ArgumentError.new('Attempt to take negative size!')
9
+ end
10
+
11
+ @how_many_to_take = how_many_to_take
12
+ @taken = 0
13
+ @closed = false
14
+ end
15
+
16
+ def call(tracker)
17
+ behaviour = lambda do |value|
18
+ return if @closed
19
+ if @taken < @how_many_to_take
20
+ tracker.on_value(value)
21
+ @taken += 1
22
+ else
23
+ @closed = true
24
+ tracker.on_close
25
+ end
26
+ end
27
+
28
+ Subscriptions::OperationSubscription.new(
29
+ tracker,
30
+ value: behaviour
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+