reacto 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c3be32892f6d090d99fad277060c0a6594eff2f
4
- data.tar.gz: 8cf5362fb0e3a1de7d6a7ab8c30fa6afd78cba97
3
+ metadata.gz: 1ce8f7dd2f7b2c680f62228680fe2dc6fcfbe5b5
4
+ data.tar.gz: 5518ae7b29f652786d20016dc3018b3d44975d8a
5
5
  SHA512:
6
- metadata.gz: 60e81798750a652579e0834810f80352c0c6fb3418c35f062ee82efd26f2ec44f9211e149324ec5da9b6e4547aaca700bac123aef4333725ee2a19eb3c63812e
7
- data.tar.gz: bc7528738e9e26c73eb513075c6ebea36870eb9c0d396672391130abfdf1862ed0a44dd1b7db751e1fcf9617ad6d432a5c558fe825e996c36535cdd207ea386a
6
+ metadata.gz: 16ea2d612d86393abfdc70c72f78369beb436a9ed14791a907e2a5771cc593bb941779bcef49b2906ad523b65e884d32fc7df6971a4ee9e7bee9da3c2c4f32f7
7
+ data.tar.gz: 747d7487a69e971d63fd100db3def9e147e0596b2d337714c3673d849705710e498bacb97ec24064ac5eb68d3cb5d2b60172f0202000e6aee207e7a7444926a7
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
2
  --require spec_helper
3
+ --order rand
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reacto (0.0.0)
4
+ reacto (0.0.1)
5
5
  concurrent-ruby (~> 1.0.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- concurrent-ruby (1.0.0)
10
+ concurrent-ruby (1.0.1)
11
11
  diff-lcs (1.2.5)
12
12
  rspec (3.3.0)
13
13
  rspec-core (~> 3.3.0)
data/README.md CHANGED
@@ -1 +1,38 @@
1
1
  # reacto
2
+
3
+ Reactive Programming for Ruby with some concurrency thrown into the mix!
4
+
5
+ ## How to install?
6
+
7
+ If you use `bundler` just add this line to your `Gemfile`:
8
+
9
+ ```ruby
10
+ gem 'reacto'
11
+ ```
12
+
13
+ Alternatively you can instal the gem with `gem install reacto` or clone this
14
+ repository and play with it!
15
+
16
+ ## Why?
17
+
18
+ Because it is very cool to be reactive these days!
19
+ Seriously - to be reactive means that your code is able to react on changes
20
+ happening in various places right away. For example third party data sources or
21
+ other parts of the code. It helps writing multy-component apps which could
22
+ handle failures and still be availale.
23
+
24
+ Of course there are other implemenations of reactive programming for ruby:
25
+
26
+ * [RxRuby](https://github.com/ReactiveX/RxRuby) : Very powerful implemenation
27
+ of RX. Handles concurrency and as a whole has more features than `Reacto`.
28
+ So why `Reacto`? `Reacto` has simpler interface it is native Ruby lib and
29
+ is easier to use it. The goal of `Reacto` is to be competitor to `RxRuby`
30
+ in the ruby world.
31
+ Still the author of reacto is big fan of `RX` especially `RxJava`. He even has
32
+ a book on the topic using `RxJava` :
33
+ [Learning Reactive Programming with Java 8](https://www.packtpub.com/application-development/learning-reactive-programming-java-8)
34
+ * [Frappuccino](https://github.com/steveklabnik/frappuccino) : Very cool lib,
35
+ easy to use and simply beautiful. The only drawback - it is a bit limitted :
36
+ no concurrency and small set of operators.
37
+ But if you don't need more complicated operators it is the best.
38
+ The author of `Reacto` highy recommends it.
@@ -0,0 +1,103 @@
1
+ require 'yaml'
2
+ require 'reacto/cache/memory'
3
+
4
+ module Reacto
5
+ module Cache
6
+ class File
7
+ attr_reader :location, :ttl
8
+ attr_reader :data
9
+
10
+ def initialize(location: nil, ttl: 60)
11
+ @location = location
12
+ @ttl = ttl
13
+
14
+ if @location.nil?
15
+ fail(
16
+ ArgumentError, 'File location is mandatory while using file cache!'
17
+ )
18
+ end
19
+ end
20
+
21
+ def ready?
22
+ return data.ready? unless data.nil?
23
+
24
+ fresh?
25
+ end
26
+
27
+ def each
28
+ return unless ready?
29
+ deserialize
30
+
31
+ data.each do |value|
32
+ yield value
33
+ end
34
+ end
35
+
36
+ def error?
37
+ return false unless ready?
38
+ deserialize
39
+
40
+ data.error?
41
+ end
42
+
43
+ def closed?
44
+ return false unless ready?
45
+ deserialize
46
+
47
+ data.closed?
48
+ end
49
+
50
+ def error
51
+ return false unless ready?
52
+ deserialize
53
+
54
+ data.error
55
+ end
56
+
57
+ def on_value(value)
58
+ init_data
59
+
60
+ data.on_value(value)
61
+ end
62
+
63
+ def on_error(error)
64
+ init_data
65
+
66
+ data.on_error(error)
67
+ serialize
68
+ end
69
+
70
+ def on_close
71
+ init_data
72
+
73
+ data.on_close
74
+ serialize
75
+ end
76
+
77
+ private
78
+
79
+ def fresh?
80
+ ::File.file?(location) && (Time.now - ::File.mtime(location)) <= ttl
81
+ end
82
+
83
+ def init_data
84
+ @data ||= Memory.new
85
+ end
86
+
87
+ def deserialize
88
+ return unless fresh?
89
+
90
+ @data ||= YAML::load(::File.read(location))
91
+ end
92
+
93
+ def serialize
94
+ return if data.nil?
95
+
96
+ ::File.open(location, 'w') do |f|
97
+ f.write(YAML::dump(data))
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
@@ -0,0 +1,44 @@
1
+ module Reacto
2
+ module Cache
3
+ class Memory
4
+ def initialize(_ = nil)
5
+ @values = []
6
+ @closed = false
7
+ end
8
+
9
+ def each
10
+ @values.each do |value|
11
+ yield value
12
+ end
13
+ end
14
+
15
+ def ready?
16
+ error? || closed?
17
+ end
18
+
19
+ def error?
20
+ @error != nil
21
+ end
22
+
23
+ def closed?
24
+ @closed
25
+ end
26
+
27
+ def error
28
+ @error
29
+ end
30
+
31
+ def on_value(value)
32
+ @values << value
33
+ end
34
+
35
+ def on_error(error)
36
+ @error = error
37
+ end
38
+
39
+ def on_close
40
+ @closed = true
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+ require 'reacto/cache/memory'
4
+ require 'reacto/cache/file'
5
+
6
+ module Reacto
7
+ module Operations
8
+ class Cache
9
+ TYPES = {
10
+ memory: Reacto::Cache::Memory,
11
+ file: Reacto::Cache::File
12
+ }
13
+
14
+ def initialize(type: :memory, **settings)
15
+ type = TYPES[type] if TYPES.key?(type)
16
+
17
+ @cache = type.new(**settings)
18
+ end
19
+
20
+ def call(tracker)
21
+ if @cache.ready?
22
+ @cache.each do |value|
23
+ tracker.on_value(value)
24
+ end
25
+
26
+ if @cache.error?
27
+ tracker.on_error(@cache.error)
28
+ else
29
+ tracker.on_close
30
+ end
31
+
32
+ NOTHING
33
+ else
34
+ Subscriptions::OperationSubscription.new(
35
+ tracker,
36
+ value: -> (v) do
37
+ @cache.on_value(v)
38
+ tracker.on_value(v)
39
+ end,
40
+ error: -> (e) do
41
+ @cache.on_error(e)
42
+ tracker.on_error(e)
43
+ end,
44
+ close: -> () do
45
+ @cache.on_close
46
+ tracker.on_close
47
+ end
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -16,9 +16,4 @@ require 'reacto/operations/flatten'
16
16
  require 'reacto/operations/throttle'
17
17
  require 'reacto/operations/flat_map'
18
18
  require 'reacto/operations/flat_map_latest'
19
-
20
- module Reacto
21
- module Operations
22
- end
23
- end
24
-
19
+ require 'reacto/operations/cache'
@@ -6,7 +6,7 @@ module Reacto
6
6
  @threads = threads
7
7
  end
8
8
  def cleanup
9
- @executor.shutdown
9
+ @executor.shutdown unless @executor.nil?
10
10
  @executor = nil
11
11
 
12
12
  @threads.each do |thread|
@@ -42,6 +42,7 @@ module Reacto
42
42
  return unless subscribed?
43
43
  return if waiting?
44
44
 
45
+
45
46
  on_value_subscriptions(val)
46
47
  after_on_value(val)
47
48
  end
@@ -69,7 +70,7 @@ module Reacto
69
70
 
70
71
  def on_close
71
72
  return unless subscribed?
72
- return unless @subscriptions.any? { |s| !s.closed? }
73
+ return if @subscriptions.any? { |s| !s.closed? }
73
74
 
74
75
  @subscriber.on_close
75
76
  unsubscribe
@@ -15,6 +15,14 @@ module Reacto
15
15
  def on_value_subscriptions(v)
16
16
  @subscriber.on_value(v)
17
17
  end
18
+
19
+ def on_close
20
+ return unless subscribed?
21
+ return unless @subscriptions.any? { |s| !s.closed? }
22
+
23
+ @subscriber.on_close
24
+ unsubscribe
25
+ end
18
26
  end
19
27
  end
20
28
  end
@@ -62,7 +62,7 @@ module Reacto
62
62
  def on_error(e)
63
63
  return unless subscribed?
64
64
 
65
- @error.call(error)
65
+ @error.call(e)
66
66
  @subscriptions.each { |s| s.on_error(e) }
67
67
  unsubscribe
68
68
  end
@@ -1,5 +1,6 @@
1
1
  require 'concurrent'
2
2
 
3
+ require 'reacto/constants'
3
4
  require 'reacto/behaviours'
4
5
  require 'reacto/subscriptions'
5
6
  require 'reacto/tracker'
@@ -92,6 +93,8 @@ module Reacto
92
93
  task = Concurrent::TimerTask.new(execution_interval: interval) do
93
94
  queue.push('ready')
94
95
  end
96
+
97
+ Thread::abort_on_exception = true
95
98
  thread = Thread.new do
96
99
  begin
97
100
  loop do
@@ -193,7 +196,8 @@ module Reacto
193
196
  operation = block_given? ? block : operation
194
197
  Trackable.new(nil, @executor) do |tracker_subscription|
195
198
  begin
196
- lift_behaviour(operation.call(tracker_subscription))
199
+ modified = operation.call(tracker_subscription)
200
+ lift_behaviour(modified) unless modified == NOTHING
197
201
  rescue Exception => e
198
202
  tracker_subscription.on_error(e)
199
203
  end
@@ -282,6 +286,11 @@ module Reacto
282
286
  lift(Operations::Throttle.new(delay))
283
287
  end
284
288
 
289
+ def cache(type: :memory, **settings)
290
+ settings ||= {}
291
+ lift(Operations::Cache.new(type: type, **settings))
292
+ end
293
+
285
294
  def track_on(executor)
286
295
  lift(Operations::TrackOn.new(executor))
287
296
  end
@@ -1,4 +1,4 @@
1
1
  module Reacto
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
4
4
 
@@ -1,216 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  context Reacto::Trackable do
4
- context '.error' do
5
- it 'creates a new trackable emitting only the error passed' do
6
- err = StandardError.new('Errrr')
7
- attach_test_trackers described_class.error(err)
8
-
9
- expect(test_data).to be == [err]
10
- end
11
- end
12
-
13
- context '.make' do
14
- it 'creates a new trackable with custom behaviour passed as lambda' do
15
- behaviour = lambda do |tracker|
16
- (1..10).each do |v|
17
- tracker.on_value(v)
18
- end
19
-
20
- tracker.on_close
21
- end
22
-
23
- attach_test_trackers described_class.make(behaviour)
24
-
25
- expect(test_data.size).to be(11)
26
- expect(test_data).to be == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '|']
27
- end
28
-
29
- it 'creates a new trackable with custom behaviour passed as block' do
30
- trackable = described_class.make do |tracker|
31
- (1..10).each do |v|
32
- tracker.on_value(v)
33
- end
34
-
35
- tracker.on_close
36
- end
37
- attach_test_trackers trackable
38
-
39
- expect(test_data.size).to be(11)
40
- expect(test_data).to be == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '|']
41
- end
42
-
43
- it 'does not emit anything once it is closed' do
44
- trackable = described_class.make do |tracker|
45
- (1..5).each do |v|
46
- tracker.on_value(v)
47
- end
48
-
49
- tracker.on_close
50
-
51
- (1..5).each do |v|
52
- tracker.on_value(v)
53
- end
54
- end
55
- attach_test_trackers trackable
56
-
57
- expect(test_data.size).to be(6)
58
- expect(test_data).to be == [1, 2, 3, 4, 5, '|']
59
- end
60
-
61
- it 'does not emit anything once it has an error' do
62
- trackable = described_class.make do |tracker|
63
- (1..5).each do |v|
64
- tracker.on_value(v)
65
- end
66
-
67
- tracker.on_error('error')
68
-
69
- (1..5).each do |v|
70
- tracker.on_value(v)
71
- end
72
- end
73
- attach_test_trackers trackable
74
-
75
- expect(test_data.size).to be(6)
76
- expect(test_data).to be == [1, 2, 3, 4, 5, 'error']
77
- end
78
-
79
- it 'emits the same behaviour for every subscribe' do
80
- trackable = described_class.make do |tracker|
81
- (1..5).each do |v|
82
- tracker.on_value(v)
83
- end
84
-
85
- tracker.on_close
86
- end
87
- attach_test_trackers trackable
88
- attach_test_trackers trackable
89
-
90
- expect(test_data.size).to be(12)
91
- expect(test_data).to be == [1, 2, 3, 4, 5, '|', 1, 2, 3, 4, 5, '|']
92
- end
93
- end
94
-
95
- context '.later' do
96
- it 'emits the passed value after the passed time runs out and then emits ' \
97
- 'a close notification' do
98
- trackable = described_class.later(0.2, 5)
99
- subscription = attach_test_trackers(trackable)
100
-
101
- expect(test_data).to be_empty
102
- trackable.await(subscription)
103
- expect(test_data).to be == [5, '|']
104
- end
105
-
106
- it 'it can use a specific executor' do
107
- trackable = described_class.later(
108
- 0.2, 5, executor: Reacto::Executors.immediate
109
- )
110
- subscription = attach_test_trackers(trackable)
111
- expect(test_data).to be == [5, '|']
112
- end
113
- end
114
-
115
- context '.interval' do
116
- it 'emits an infinite sequence of number on every n seconds by default' do
117
- trackable = described_class.interval(0.1)
118
- subscription = attach_test_trackers(trackable)
119
- sleep 1
120
- subscription.unsubscribe
121
-
122
- expect(test_data).to be == (0..8).to_a
123
- end
124
-
125
- it 'can use any enumerator to produce the sequence to emit' do
126
- trackable = described_class.interval(0.1, ('a'..'z').each)
127
- subscription = attach_test_trackers(trackable)
128
- sleep 1
129
- subscription.unsubscribe
130
-
131
- expect(test_data).to be == ('a'..'i').to_a
132
- end
133
-
134
- it 'can use the immediate executor to block the current thread while ' \
135
- 'emitting' do
136
- trackable = described_class.interval(
137
- 0.1, (1..5).each, executor: Reacto::Executors.immediate
138
- )
139
- subscription = attach_test_trackers(trackable)
140
-
141
- expect(test_data).to be == (1..5).to_a + ['|']
142
- end
143
- end
144
-
145
- context '.value' do
146
- it 'emits only the passed value and then closes' do
147
- trackable = described_class.value(5)
148
- subscription = attach_test_trackers(trackable)
149
-
150
- expect(test_data).to be == [5, '|']
151
- end
152
- end
153
-
154
- context '.enumerable' do
155
- it 'emits the whole enumerable one-by-one and then closes' do
156
- trackable = described_class.enumerable([1, 3, 5, 6, 7, 8])
157
- subscription = attach_test_trackers(trackable)
158
-
159
- expect(test_data).to be == [1, 3, 5, 6, 7, 8, '|']
160
- end
161
-
162
- it 'on error it emits all til the error and the error' do
163
- class PositiveArray
164
- include Enumerable
165
-
166
- def error
167
- @error ||= StandardError.new('Bad.')
168
- end
169
-
170
- def initialize(*members)
171
- @members = members
172
- end
173
-
174
- def each(&block)
175
- @members.each do |member|
176
- raise error if member < 0
177
- block.call(member)
178
- end
179
- end
180
- end
181
-
182
- enumerable = PositiveArray.new(2, 3, -4, 3, 6)
183
- trackable = described_class.enumerable(enumerable)
184
- subscription = attach_test_trackers(trackable)
185
-
186
- expect(test_data).to be == [2, 3, enumerable.error]
187
-
188
- end
189
- end
190
-
191
- context '.combine' do
192
- it 'combines the notifications of Trackables with different number of ' \
193
- 'notifications using the passed combinator' do
194
- trackable1 = described_class.interval(0.3).take(4)
195
- trackable2 = described_class.interval(0.7, ('a'..'b').each)
196
- trackable3 = described_class.interval(0.5, ('A'..'C').each)
197
-
198
- trackable = described_class.combine(
199
- trackable1, trackable2, trackable3
200
- ) do |v1, v2, v3|
201
- "#{v1} : #{v2} : #{v3}"
202
- end
203
-
204
- subscription = attach_test_trackers(trackable)
205
- trackable.await(subscription)
206
-
207
- expect(test_data).to be == [
208
- '1 : a : A', '2 : a : A', '2 : a : B', '3 : a : B',
209
- '3 : b : B', '3 : b : C', '|'
210
- ]
211
- end
212
- end
213
-
214
4
  context '.combine_last' do
215
5
  it 'combines the notifications of Trackables based on their ' \
216
6
  'sequence number - the first notification of the sources, then ' \
@@ -1,11 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  context Reacto::Trackable do
4
-
5
- let(:test_data) { [] }
6
- let(:test_on_value) { -> (v) { test_data << v }}
7
- let(:test_on_close) { -> () { test_data << '|' }}
8
-
9
4
  subject do
10
5
  described_class.new do |tracker_subscription|
11
6
  tracker_subscription.on_value(16)
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ let(:source_updates) { [] }
5
+ let(:source) do
6
+ Reacto::Trackable.make do |tracker|
7
+ source_updates << true
8
+
9
+ tracker.on_value(3)
10
+ tracker.on_value(4)
11
+ tracker.on_value(5)
12
+ tracker.on_close
13
+ end
14
+ end
15
+
16
+ def check_cache(trackable)
17
+ trackable.on(value: test_on_value, close: test_on_close)
18
+ expect(test_data).to be == [3, 4, 5, '|']
19
+ expect(source_updates).to be == [true]
20
+
21
+ trackable.on(value: test_on_value, close: test_on_close)
22
+ expect(test_data).to be == [3, 4, 5, '|', 3, 4, 5, '|']
23
+ expect(source_updates).to be == [true]
24
+ end
25
+
26
+ context '#cache' do
27
+ context 'memory' do
28
+ it 'caches all the values received before the closing notifincation and ' \
29
+ 'replayes them on subsequent subscriptions' do
30
+ check_cache(source.cache)
31
+ end
32
+ end
33
+
34
+ context 'file' do
35
+ after(:each) do
36
+ File.delete('tmp/test.cache')
37
+ end
38
+
39
+ it 'caches all the values received before the closing notifincation and ' \
40
+ 'replayes them on subsequent subscriptions' do
41
+ check_cache(source.cache(type: :file, location: 'tmp/test.cache'))
42
+ end
43
+
44
+ it 'uses the cache only if it is fresh' do
45
+ trackable1 =
46
+ source.cache(type: :file, location: 'tmp/test.cache', ttl: 0.5)
47
+
48
+ check_cache(trackable1)
49
+
50
+ trackable2 =
51
+ source.cache(type: :file, location: 'tmp/test.cache', ttl: 0.5)
52
+ trackable2.on
53
+ expect(source_updates).to be == [true]
54
+
55
+ sleep(1)
56
+
57
+ trackable3 =
58
+ source.cache(type: :file, location: 'tmp/test.cache', ttl: 0.5)
59
+ trackable3.on
60
+ expect(source_updates).to be == [true] * 2
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.enumerable' do
5
+ it 'emits the whole enumerable one-by-one and then closes' do
6
+ trackable = described_class.enumerable([1, 3, 5, 6, 7, 8])
7
+ subscription = attach_test_trackers(trackable)
8
+
9
+ expect(test_data).to be == [1, 3, 5, 6, 7, 8, '|']
10
+ end
11
+
12
+ it 'on error it emits all til the error and the error' do
13
+ class PositiveArray
14
+ include Enumerable
15
+
16
+ def error
17
+ @error ||= StandardError.new('Bad.')
18
+ end
19
+
20
+ def initialize(*members)
21
+ @members = members
22
+ end
23
+
24
+ def each(&block)
25
+ @members.each do |member|
26
+ raise error if member < 0
27
+ block.call(member)
28
+ end
29
+ end
30
+ end
31
+
32
+ enumerable = PositiveArray.new(2, 3, -4, 3, 6)
33
+ trackable = described_class.enumerable(enumerable)
34
+ subscription = attach_test_trackers(trackable)
35
+
36
+ expect(test_data).to be == [2, 3, enumerable.error]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.error' do
5
+ it 'creates a new trackable emitting only the error passed' do
6
+ err = StandardError.new('Errrr')
7
+ attach_test_trackers described_class.error(err)
8
+
9
+ expect(test_data).to be == [err]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.interval' do
5
+ it 'emits an infinite sequence of number on every n seconds by default' do
6
+ trackable = described_class.interval(0.1)
7
+ subscription = attach_test_trackers(trackable)
8
+ sleep 1
9
+ subscription.unsubscribe
10
+
11
+ expect(test_data).to be == (0..8).to_a
12
+ end
13
+
14
+ it 'can use any enumerator to produce the sequence to emit' do
15
+ trackable = described_class.interval(0.1, ('a'..'z').each)
16
+ subscription = attach_test_trackers(trackable)
17
+ sleep 1
18
+ subscription.unsubscribe
19
+
20
+ expect(test_data).to be == ('a'..'i').to_a
21
+ end
22
+
23
+ it 'can use the immediate executor to block the current thread while ' \
24
+ 'emitting' do
25
+ trackable = described_class.interval(
26
+ 0.1, (1..5).each, executor: Reacto::Executors.immediate
27
+ )
28
+ subscription = attach_test_trackers(trackable)
29
+
30
+ expect(test_data).to be == (1..5).to_a + ['|']
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.later' do
5
+ it 'emits the passed value after the passed time runs out and then emits ' \
6
+ 'a close notification' do
7
+ trackable = described_class.later(0.2, 5)
8
+ subscription = attach_test_trackers(trackable)
9
+
10
+ expect(test_data).to be_empty
11
+ trackable.await(subscription)
12
+ expect(test_data).to be == [5, '|']
13
+ end
14
+
15
+ it 'it can use a specific executor' do
16
+ trackable = described_class.later(
17
+ 0.2, 5, executor: Reacto::Executors.immediate
18
+ )
19
+ subscription = attach_test_trackers(trackable)
20
+ expect(test_data).to be == [5, '|']
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.make' do
5
+ it 'creates a new trackable with custom behaviour passed as lambda' do
6
+ behaviour = lambda do |tracker|
7
+ (1..10).each do |v|
8
+ tracker.on_value(v)
9
+ end
10
+
11
+ tracker.on_close
12
+ end
13
+
14
+ attach_test_trackers described_class.make(behaviour)
15
+
16
+ expect(test_data.size).to be(11)
17
+ expect(test_data).to be == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '|']
18
+ end
19
+
20
+ it 'creates a new trackable with custom behaviour passed as block' do
21
+ trackable = described_class.make do |tracker|
22
+ (1..10).each do |v|
23
+ tracker.on_value(v)
24
+ end
25
+
26
+ tracker.on_close
27
+ end
28
+ attach_test_trackers trackable
29
+
30
+ expect(test_data.size).to be(11)
31
+ expect(test_data).to be == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '|']
32
+ end
33
+
34
+ it 'does not emit anything once it is closed' do
35
+ trackable = described_class.make do |tracker|
36
+ (1..5).each do |v|
37
+ tracker.on_value(v)
38
+ end
39
+
40
+ tracker.on_close
41
+
42
+ (1..5).each do |v|
43
+ tracker.on_value(v)
44
+ end
45
+ end
46
+ attach_test_trackers trackable
47
+
48
+ expect(test_data.size).to be(6)
49
+ expect(test_data).to be == [1, 2, 3, 4, 5, '|']
50
+ end
51
+
52
+ it 'does not emit anything once it has an error' do
53
+ trackable = described_class.make do |tracker|
54
+ (1..5).each do |v|
55
+ tracker.on_value(v)
56
+ end
57
+
58
+ tracker.on_error('error')
59
+
60
+ (1..5).each do |v|
61
+ tracker.on_value(v)
62
+ end
63
+ end
64
+ attach_test_trackers trackable
65
+
66
+ expect(test_data.size).to be(6)
67
+ expect(test_data).to be == [1, 2, 3, 4, 5, 'error']
68
+ end
69
+
70
+ it 'emits the same behaviour for every subscribe' do
71
+ trackable = described_class.make do |tracker|
72
+ (1..5).each do |v|
73
+ tracker.on_value(v)
74
+ end
75
+
76
+ tracker.on_close
77
+ end
78
+ attach_test_trackers trackable
79
+ attach_test_trackers trackable
80
+
81
+ expect(test_data.size).to be(12)
82
+ expect(test_data).to be == [1, 2, 3, 4, 5, '|', 1, 2, 3, 4, 5, '|']
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ context Reacto::Trackable do
4
+ context '.value' do
5
+ it 'emits only the passed value and then closes' do
6
+ trackable = described_class.value(5)
7
+ subscription = attach_test_trackers(trackable)
8
+
9
+ expect(test_data).to be == [5, '|']
10
+ end
11
+ end
12
+ end
@@ -13,39 +13,4 @@ context Reacto::Trackable do
13
13
  expect(test_data[0]).to be(4)
14
14
  end
15
15
  end
16
-
17
- context '#on' do
18
- it 'returns a Reacto::Subscription' do
19
- actual = described_class.new(Reacto::NO_ACTION).on
20
-
21
- expect(actual).to_not be(nil)
22
- expect(actual).to be_kind_of(Reacto::Subscriptions::Subscription)
23
- end
24
-
25
- context('value') do
26
- it 'the trackable behavior uses a subscription which `on_value` ' \
27
- 'is the passed value action' do
28
- described_class.new(test_behaviour).on(value: test_on_value)
29
-
30
- expect(test_data.size).to be(1)
31
- expect(test_data[0]).to be(5)
32
- end
33
- end
34
- end
35
-
36
- context '#lift' do
37
- it 'applies a transformation to the trackable behaviour' do
38
- lifted_trackable = source.lift do |tracker_subscription|
39
- Reacto::Subscriptions::OperationSubscription.new(
40
- tracker_subscription,
41
- value: -> (v) { tracker_subscription.on_value(v * v) }
42
- )
43
- end
44
-
45
- lifted_trackable.on(value: test_on_value)
46
-
47
- expect(test_data.size).to be(1)
48
- expect(test_data[0]).to be(25)
49
- end
50
- end
51
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reacto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nickolay Tzvetinov - Meddle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-10 00:00:00.000000000 Z
11
+ date: 2016-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,10 +66,13 @@ files:
66
66
  - README.md
67
67
  - lib/reacto.rb
68
68
  - lib/reacto/behaviours.rb
69
+ - lib/reacto/cache/file.rb
70
+ - lib/reacto/cache/memory.rb
69
71
  - lib/reacto/constants.rb
70
72
  - lib/reacto/executors.rb
71
73
  - lib/reacto/operations.rb
72
74
  - lib/reacto/operations/buffer.rb
75
+ - lib/reacto/operations/cache.rb
73
76
  - lib/reacto/operations/concat.rb
74
77
  - lib/reacto/operations/diff.rb
75
78
  - lib/reacto/operations/drop.rb
@@ -111,7 +114,14 @@ files:
111
114
  - spec/reacto/executors_and_trackable_spec.rb
112
115
  - spec/reacto/operations/track_on_spec.rb
113
116
  - spec/reacto/trackable/buffer_spec.rb
117
+ - spec/reacto/trackable/cache_spec.rb
118
+ - spec/reacto/trackable/class_level/enumerable_spec.rb
119
+ - spec/reacto/trackable/class_level/error_spec.rb
120
+ - spec/reacto/trackable/class_level/interval_spec.rb
121
+ - spec/reacto/trackable/class_level/later_spec.rb
122
+ - spec/reacto/trackable/class_level/make_spec.rb
114
123
  - spec/reacto/trackable/class_level/never_spec.rb
124
+ - spec/reacto/trackable/class_level/value_spec.rb
115
125
  - spec/reacto/trackable/concat_spec.rb
116
126
  - spec/reacto/trackable/diff_spec.rb
117
127
  - spec/reacto/trackable/drop_errors_spec.rb
@@ -156,7 +166,14 @@ test_files:
156
166
  - spec/reacto/executors_and_trackable_spec.rb
157
167
  - spec/reacto/operations/track_on_spec.rb
158
168
  - spec/reacto/trackable/buffer_spec.rb
169
+ - spec/reacto/trackable/cache_spec.rb
170
+ - spec/reacto/trackable/class_level/enumerable_spec.rb
171
+ - spec/reacto/trackable/class_level/error_spec.rb
172
+ - spec/reacto/trackable/class_level/interval_spec.rb
173
+ - spec/reacto/trackable/class_level/later_spec.rb
174
+ - spec/reacto/trackable/class_level/make_spec.rb
159
175
  - spec/reacto/trackable/class_level/never_spec.rb
176
+ - spec/reacto/trackable/class_level/value_spec.rb
160
177
  - spec/reacto/trackable/concat_spec.rb
161
178
  - spec/reacto/trackable/diff_spec.rb
162
179
  - spec/reacto/trackable/drop_errors_spec.rb