publican 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4904aa4b07d98990de7b90899ddd1929894a94a5
4
+ data.tar.gz: 615f70556ec38f6f0b8aa3384856b7e3ce6ae814
5
+ SHA512:
6
+ metadata.gz: 1b4b09c539ffa17c823cf05b3d2be170d213e44d247760dc2ad135aa1be8e18e534a160deeb00fc73512bad4fb8b1e382c42fed0a67d3d83519e21f8a5641b35
7
+ data.tar.gz: cdec61a0f38a303b2d13c922ad35b49098f46107a779b190f0825cf990124f64708312d3d70868ccbf7f34c20e1d6ff5ec67062db743758a1aefaaac21ced1d2
@@ -0,0 +1 @@
1
+ *.gem
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright 2015 Powershop New Zealand Limited, Roger Nesbitt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Publican
2
+
3
+ A super-simple subscribe/publish library for your Ruby objects.
4
+
5
+ Often you want to let your caller know what happened in your method call. You might return a symbol or custom-made
6
+ Struct, and expect your caller to use a `case` statement to work out what happened. The problem is that if you
7
+ return a symbol the caller isn't expecting, or if the caller types in the symbol incorrectly, they might not handle
8
+ it correctly.
9
+
10
+ Another traditional way to handle this is to make an exception classes for every outcome. That's pretty
11
+ code-heavy, and it doesn't let you notify that multiple events happened either.
12
+
13
+ Enter Publican. Your method can publish events for its callers to listen to, with as much additional data as you
14
+ like. You can publish as many events as you like, as well as progress upgrades if it's long-running. You can have
15
+ multiple listeners. You can also guarantee that your callers are listening for important events.
16
+
17
+ ## Using Publican
18
+
19
+ Publican can just be `include`d into a class. By doing so, it adds one public method, `on`, and two protected
20
+ methods, `publish` and `publish!`.
21
+
22
+ Here's an example of Publican in use. An ideal use case is a service class:
23
+
24
+ ```ruby
25
+ class SaveImage
26
+ include Publican
27
+
28
+ def call(image)
29
+ if image.width < 300
30
+ publish!(:invalid_size, "Image must be at least 300 pixels wide")
31
+ return
32
+ end
33
+
34
+ if image.height < 300
35
+ image.resize!(height: 300)
36
+ publish(:resized)
37
+ end
38
+
39
+ File.write("image_data/#{image.id}", image.data)
40
+ publish(:success)
41
+ end
42
+ end
43
+ ```
44
+
45
+ You'll notice that, instead of using return values, we're publishing events for our subscribers to hear.
46
+
47
+ Here's some code that uses our Publican-powered service:
48
+
49
+ ```ruby
50
+ SaveImage
51
+ .new
52
+ .on(:invalid_size) { |error| puts "Error: #{error}" }
53
+ .on(:resized) { puts "The image was resized" }
54
+ .on(:success) { puts "Everything was awesome!" }
55
+ .call(image)
56
+ ```
57
+
58
+ Or maybe you'd like to use the same service in your Rails app? No problem.
59
+
60
+ ```ruby
61
+ class ImagesController < ApplicationController
62
+ def create
63
+ image = Image.find(params[:id])
64
+
65
+ SaveImage
66
+ .new
67
+ .on(:invalid_size) { |error| flash.alert = "Error: #{error}" }
68
+ .on(:success) { flash.notice = "Image successfully saved." }
69
+ .call(image)
70
+
71
+ redirect_to root_path
72
+ end
73
+ end
74
+ ```
75
+
76
+ Note that the above example doesn't listen to the `:resized` event. That's OK, because the event was published
77
+ with the `publish` method, not `publish!`. What's the difference? See the next section.
78
+
79
+ ## publish vs publish!
80
+
81
+ `publish` sends out your event and doesn't care whether anyone's listening.
82
+
83
+ `publish!` ensures at least something is listening to the event, otherwise it'll raise
84
+ a `Publican::NoListenersError` exception. This is useful when you want to ensure some action is taken on the
85
+ event occurring.
86
+
87
+ ## Licence and Copyright
88
+
89
+ Copyright Powershop New Zealand Limited and Roger Nesbitt. MIT licence.
@@ -0,0 +1,25 @@
1
+ module Publican
2
+ NoListenersError = Class.new(StandardError)
3
+
4
+ def on(*events, &block)
5
+ @_publican_listeners ||= {}
6
+
7
+ events.each do |event|
8
+ (@_publican_listeners[event] ||= []) << block
9
+ end
10
+
11
+ self
12
+ end
13
+
14
+ protected
15
+
16
+ def publish(event, *args)
17
+ listeners = @_publican_listeners && @_publican_listeners[event] || []
18
+ listeners.each { |listener| listener.call(*args) }
19
+ !listeners.empty?
20
+ end
21
+
22
+ def publish!(event, *args)
23
+ publish(event, *args) or raise NoListenersError, "No listeners are listening for event #{event}"
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ spec = Gem::Specification.new do |gem|
2
+ gem.name = 'publican'
3
+ gem.version = '1.0.0'
4
+ gem.summary = 'Simple subscribe/publish library for your Ruby objects'
5
+ gem.description = <<EOT
6
+ Simple subscribe/publish library for your Ruby objects. Instead of returning symbols or raising custom-made exceptions,
7
+ let your callers know what happened in your method by publishing events. You can choose whether these events can be
8
+ ignored or whether the caller must listen to them.
9
+ EOT
10
+ gem.has_rdoc = false
11
+ gem.author = "Roger Nesbitt"
12
+ gem.email = "roger@seriousorange.com"
13
+ gem.homepage = "http://github.com/mogest/publican"
14
+ gem.license = 'MIT'
15
+
16
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ gem.files = `git ls-files`.split("\n")
18
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.required_ruby_version = '>= 1.9.3'
22
+
23
+ gem.add_development_dependency "rspec", "~> 3.0"
24
+ gem.add_development_dependency "rake", "~> 10.1"
25
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../lib/publican'
2
+
3
+ RSpec.describe Publican do
4
+ class Sample
5
+ include Publican
6
+
7
+ def fire(event, *args)
8
+ publish(event, *args)
9
+ end
10
+
11
+ def fire!(event, *args)
12
+ publish!(event, *args)
13
+ end
14
+ end
15
+
16
+ let(:sample) { Sample.new }
17
+ let(:tracker) { Struct.new(:ok, :count).new(false, 0) }
18
+
19
+ def ok
20
+ tracker.ok = true
21
+ tracker.count += 1
22
+ end
23
+
24
+ def expect_ok
25
+ expect(tracker.ok).to be true
26
+ end
27
+
28
+ it "publishes an event with no arguments" do
29
+ sample.on(:a) { ok }
30
+ sample.fire(:a)
31
+ expect_ok
32
+ end
33
+
34
+ it "publishes an event with arguments" do
35
+ sample.on(:a) {|arg| ok if arg == "hello"}
36
+ sample.fire(:a, "hello")
37
+ expect_ok
38
+ end
39
+
40
+ it "publishes an event where multiple events were defined in the on block" do
41
+ sample.on(:a, :b) { ok }
42
+ sample.fire(:b)
43
+ expect_ok
44
+ end
45
+
46
+ it "does nothing if no events have been declared for a fired event" do
47
+ sample.on(:b) { raise }
48
+ sample.fire(:a)
49
+ end
50
+
51
+ it "handles multiple event definitions for a single event" do
52
+ sample.on(:a) { ok }
53
+ sample.on(:a) { ok }
54
+ sample.fire(:a)
55
+ expect(tracker.count).to eq 2
56
+ end
57
+
58
+ it "publishes an event, ensuring that there are listeners that are going to respond" do
59
+ sample.on(:a) { ok }
60
+ sample.fire!(:a)
61
+ expect_ok
62
+ end
63
+
64
+ it "raises if an event published using publish! has no listeners" do
65
+ expect { sample.fire!(:a) }.to raise_error(Publican::NoListenersError, "No listeners are listening for event a")
66
+ end
67
+
68
+ describe "#on" do
69
+ it "returns self for method chaining" do
70
+ expect(sample.on(:a) { ok }).to eq(sample)
71
+ end
72
+ end
73
+
74
+ describe "#publish" do
75
+ it "returns true when someone was listening" do
76
+ sample.on(:a) { }
77
+ expect(sample.fire(:a)).to be true
78
+ end
79
+
80
+ it "returns false when no-one was listening" do
81
+ expect(sample.fire(:a)).to be false
82
+ end
83
+ end
84
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: publican
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Roger Nesbitt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.1'
41
+ description: |
42
+ Simple subscribe/publish library for your Ruby objects. Instead of returning symbols or raising custom-made exceptions,
43
+ let your callers know what happened in your method by publishing events. You can choose whether these events can be
44
+ ignored or whether the caller must listen to them.
45
+ email: roger@seriousorange.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - LICENSE
52
+ - README.md
53
+ - lib/publican.rb
54
+ - publican.gemspec
55
+ - spec/publican_spec.rb
56
+ homepage: http://github.com/mogest/publican
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.9.3
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.4.8
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Simple subscribe/publish library for your Ruby objects
80
+ test_files:
81
+ - spec/publican_spec.rb