flirt 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -1
- data/lib/flirt.rb +3 -2
- data/lib/flirt/callback.rb +17 -4
- data/lib/flirt/version.rb +1 -1
- data/spec/flirt/{flirt_spec.rb → integration/flirt_spec.rb} +0 -1
- data/spec/flirt/integration/weak_references_spec.rb +46 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de9f01302ff30750a5b6296be545b70995dcf74c
|
4
|
+
data.tar.gz: 42ef6708146d05a0dad9690563b8315866cab132
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6191551f0485582281e9b8c43ec865a18bdb8945131a8d527925efebc040dcf87a31531ff7c9b48ddb375e1486cea48e5068c43a291918f45da878e7beae695
|
7
|
+
data.tar.gz: b84e6e18cc19f5a6d6fdc18f5dd8de7e5f05795516e6c683c0d481154f7105f546ff13abd3d0437112f0f793e147fa85cd607808b80bea7a0b2a8ff1d1427893
|
data/README.md
CHANGED
@@ -168,14 +168,63 @@ With such a simple syntax, it's easy to understand what Flirt is doing when you
|
|
168
168
|
|
169
169
|
There is no set-up beyond requiring the gem.
|
170
170
|
|
171
|
-
Events are
|
171
|
+
Events are canonly be represented as symbols, helping to spot and squash certain kinds of bugs early.
|
172
172
|
|
173
173
|
Only one object - Flirt - can be listened to, reducing the danger of implicit coupling between publishers and subscribers. Subscribers listen to events, not objects.
|
174
174
|
|
175
|
+
### Flirt doesn't use threads or persistence frameworks.
|
176
|
+
|
177
|
+
This means events are fired in a deterministic way, without over-obfuscating the control flow for debug tools. You can depend on listeners being called before, for example, the end of a controler call. If you wish to delegate a task to a worker task (like Sidekiq for example) it's easy enough to do in a listener.
|
178
|
+
|
175
179
|
### Flirt has a great name
|
176
180
|
|
177
181
|
Seriously, why use any other gem when you could be flirting instead?
|
178
182
|
|
183
|
+
## Antipatterns amd misuse
|
184
|
+
|
185
|
+
#### Clearing and disabling
|
186
|
+
|
187
|
+
The ```clear```, ```enable``` and ```disable``` features are provided to aid testing. If you find yourself reaching for them in production code you're probably using the wrong pattern, or you may need to re-think your architecture.
|
188
|
+
|
189
|
+
Have a look at decorators if you need to add different functionality to a model depending on where it's called.
|
190
|
+
|
191
|
+
Alternatively, change the location in the code where you publish your events. A useful move in Rails is from the model to the controller, to avoid admin or other background updates triggering events that should only be based on user actions. This move can also help break some event loops, where a side effect of one event causes another event to fire and vice versa.
|
192
|
+
|
193
|
+
#### Garbage collection
|
194
|
+
|
195
|
+
If you find yourself creating and throwing away multiple listener objects, Ruby will still keep references to those objects for the lifetime of the application. This can result in memory leaks, as Ruby will not be able to garbage collect the listeners. To avoid this situation, ensure you unsubscribe from any events when you're done with them. Alternatively, on subscription you can request that Flirt uses weak references:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
Flirt.subscribe object, :flipped, with: :flipped_callback, weakref: true
|
199
|
+
```
|
200
|
+
|
201
|
+
This means that garbage collection can target the listener. Be careful though, as the listener will be garbage collected unless you keep a reference to it somewhere else. This kind of code would be problematic:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
def set_listener
|
205
|
+
Flirt.subscribe TossedListener.new, :tossed, with: :tossed_callback, weakref: true
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
This will lead to intermittent bugs as nothing keeps a reference to ```TossedListener.new```. The listener will work until garbage collection kicks in.
|
210
|
+
|
211
|
+
Flirt defaults to using strong references to ensure consistent behaviour.
|
212
|
+
|
213
|
+
## Set-up
|
214
|
+
|
215
|
+
While no set-up is required for Flirt use, there is a quirk of Rails autoloading that could cause issues.
|
216
|
+
|
217
|
+
If you wish to use listeners on a class level, you must make sure the class is loaded. If the class definition has not been required, the listener will not be registered.
|
218
|
+
|
219
|
+
Rails auto-loads classes when they're first referenced so unless you take an extra step, class-level listeners won't ever be loaded.
|
220
|
+
|
221
|
+
Placing the following code in an initializer will ensure that anything you place in the ```#{Rails.root}/app/listeners``` directory will get loaded when the Rails framework boots up:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
# /config/initializers/flirt.rb
|
225
|
+
Dir["#{Rails.root}/app/listeners/**/*.rb"].each {|file| require file }
|
226
|
+
```
|
227
|
+
|
179
228
|
##Testing
|
180
229
|
|
181
230
|
In order to test units of behaviour, you probably want to disable Flirt and test each unit of your code in isolation. You can always enable Flirt or individual events in your test file for integration tests.
|
data/lib/flirt.rb
CHANGED
@@ -23,8 +23,9 @@ module Flirt
|
|
23
23
|
|
24
24
|
def subscribe(object, event_name, options = {})
|
25
25
|
check_subscription_arguments(event_name, object, options)
|
26
|
-
callback = Flirt::Callback.new object:
|
27
|
-
callback_name: options[:with]
|
26
|
+
callback = Flirt::Callback.new object: object,
|
27
|
+
callback_name: options[:with],
|
28
|
+
weakref: options[:weakref]
|
28
29
|
add_callback(event_name, callback)
|
29
30
|
end
|
30
31
|
|
data/lib/flirt/callback.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
1
3
|
# Represents a single callback. Contains knowledge of the callback name and object
|
2
4
|
# and contains a method for calling the callback.
|
3
5
|
|
@@ -5,21 +7,32 @@ module Flirt
|
|
5
7
|
|
6
8
|
class Callback
|
7
9
|
|
8
|
-
attr_accessor :object, :callback_name
|
10
|
+
attr_accessor :object, :callback_name, :callback_object_id, :weakref
|
9
11
|
|
10
12
|
def initialize(opts = {})
|
11
|
-
self.callback_name
|
12
|
-
|
13
|
+
self.callback_name = opts.fetch(:callback_name)
|
14
|
+
callback_object = opts.fetch(:object)
|
15
|
+
self.weakref = !!opts[:weakref]
|
16
|
+
self.object = weakref ? WeakRef.new(callback_object) : callback_object
|
17
|
+
self.callback_object_id = callback_object.object_id
|
13
18
|
end
|
14
19
|
|
15
20
|
|
16
21
|
def call(event_data)
|
22
|
+
return unless alive?
|
17
23
|
object.send callback_name, event_data
|
18
24
|
end
|
19
25
|
|
20
26
|
|
27
|
+
def alive?
|
28
|
+
return true unless weakref
|
29
|
+
object.weakref_alive?
|
30
|
+
end
|
31
|
+
|
32
|
+
|
21
33
|
def ==(other_callback)
|
22
|
-
|
34
|
+
callback_object_id == other_callback.callback_object_id &&
|
35
|
+
callback_name == other_callback.callback_name
|
23
36
|
end
|
24
37
|
|
25
38
|
end
|
data/lib/flirt/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'weakref'
|
3
|
+
|
4
|
+
describe Flirt do
|
5
|
+
|
6
|
+
describe "with one strongly referenced subscriber" do
|
7
|
+
|
8
|
+
let(:event) { :event }
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
klass = Struct.new(:callback)
|
12
|
+
inst = klass.new
|
13
|
+
@weakref_of_strongref = WeakRef.new(inst)
|
14
|
+
Flirt.subscribe inst, event, with: :"callback="
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "and with one weakly referenced subscriber" do
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
klass = Struct.new(:callback)
|
22
|
+
inst = klass.new
|
23
|
+
@weakref = WeakRef.new(inst)
|
24
|
+
Flirt.subscribe inst, event, with: :"callback=", weakref: true
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
it "should not throw an exception on publish" do
|
29
|
+
GC.start
|
30
|
+
Flirt.publish event, :response
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it "should use a weak reference when weakref option set" do
|
35
|
+
GC.start
|
36
|
+
expect(@weakref.weakref_alive?).not_to eq(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
it "should not use a weak reference when weakref option not set" do
|
41
|
+
GC.start
|
42
|
+
expect(@weakref_of_strongref.weakref_alive?).to eq(true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flirt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Randles-Dunkley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,7 +73,8 @@ files:
|
|
73
73
|
- lib/flirt/version.rb
|
74
74
|
- spec/flirt/flirt_callback_spec.rb
|
75
75
|
- spec/flirt/flirt_listener_spec.rb
|
76
|
-
- spec/flirt/flirt_spec.rb
|
76
|
+
- spec/flirt/integration/flirt_spec.rb
|
77
|
+
- spec/flirt/integration/weak_references_spec.rb
|
77
78
|
- spec/spec_helper.rb
|
78
79
|
homepage: https://github.com/chemica/flirt
|
79
80
|
licenses:
|
@@ -95,12 +96,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
96
|
version: '0'
|
96
97
|
requirements: []
|
97
98
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.2.2
|
99
100
|
signing_key:
|
100
101
|
specification_version: 4
|
101
102
|
summary: A brutally simple take on the observer pattern.
|
102
103
|
test_files:
|
103
104
|
- spec/flirt/flirt_callback_spec.rb
|
104
105
|
- spec/flirt/flirt_listener_spec.rb
|
105
|
-
- spec/flirt/flirt_spec.rb
|
106
|
+
- spec/flirt/integration/flirt_spec.rb
|
107
|
+
- spec/flirt/integration/weak_references_spec.rb
|
106
108
|
- spec/spec_helper.rb
|