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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 034106def78faf22575247bb9ade0a9efe6fd2fc
4
- data.tar.gz: 3c8de9cf124629172f5873d8608186cfec4a03dd
3
+ metadata.gz: de9f01302ff30750a5b6296be545b70995dcf74c
4
+ data.tar.gz: 42ef6708146d05a0dad9690563b8315866cab132
5
5
  SHA512:
6
- metadata.gz: 0d283374e38b19974d41218ac869a303d574144b32e2dcc3ec07466cb557e2c63856cc8c598096fca966a744cb00a077962836a1f2488ed399f5e6e1f95781bc
7
- data.tar.gz: 90d7fe9305f4b7caea95d077a9be45b6141a10162e4ee3f75cb45bc38a0166ecc53629d16ff51e942e23b27b497da2e35297ae0663bc460113b0e713fc1fadcb
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 only allowed to be represented as symbols. Using strings or other objects will result in an exception, helping to spot and squash certain kinds of bugs early.
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: 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
 
@@ -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 = opts.fetch(:callback_name)
12
- self.object = opts.fetch(:object)
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
- object == other_callback.object && callback_name == other_callback.callback_name
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
@@ -1,5 +1,5 @@
1
1
  module Flirt
2
2
 
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
 
5
5
  end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- #require 'flirt/flirt_test_classes'
3
2
 
4
3
  describe Flirt do
5
4
 
@@ -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.1.1
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-03-30 00:00:00.000000000 Z
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.4.1
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