heed 0.0.7

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0345d1edb02b2cf539eec0ab3dd83d53b64ae55b
4
+ data.tar.gz: 6e49bd332ad4e1e77980a745ec3fa369fe2e263f
5
+ SHA512:
6
+ metadata.gz: 4ed74d4af4b784f28e102e3f9828aec13772078d5e4a2555019c1476385a6ecb85e8a9651f4b1b9060dd662673e419ebf60c3ff9ea8460101a45340f1093e03b
7
+ data.tar.gz: b5f84d357dad9563f3eca222f0dbcca63096dffea597640e4779f0ea2a16b11e8539b38a1e2b07b40bd531e73eccff6360ec0b871744f3a822b50cfaa305d069
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.3
6
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hark.gemspec
4
+ gemspec
data/History.md ADDED
@@ -0,0 +1,26 @@
1
+ # History
2
+
3
+ ## 0.0.7
4
+
5
+ * Change gem name to 'heed'
6
+
7
+ ## 0.0.6
8
+
9
+ * remove Kernel#to_hark
10
+
11
+ ## 0.0.5
12
+
13
+ * ruby 1.8.7 compat
14
+
15
+ ## 0.0.4
16
+
17
+ * Adds Kernel#hearken
18
+ * Adds coveralls & History
19
+
20
+ ## 0.0.3
21
+
22
+ * Adds ruby < 1.9 support
23
+
24
+ ## 0.0.1 - 0.0.2
25
+
26
+ * Initial release, slims API down to Kernel#hark
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ian White
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # Hark
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ianwhite-hark.png)](https://rubygems.org/gems/ianwhite-hark)
4
+ [![Build Status](https://travis-ci.org/ianwhite/hark.png)](https://travis-ci.org/ianwhite/hark)
5
+ [![Dependency Status](https://gemnasium.com/ianwhite/hark.png)](https://gemnasium.com/ianwhite/hark)
6
+ [![Code Climate](https://codeclimate.com/github/ianwhite/hark.png)](https://codeclimate.com/github/ianwhite/hark)
7
+ [![Coverage Status](https://coveralls.io/repos/ianwhite/hark/badge.png)](https://coveralls.io/r/ianwhite/hark)
8
+
9
+ Create a ad-hoc listeners with hark.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'ianwhite-hark', :require => 'hark'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install ianwhite-hark
24
+
25
+ ## What & Why?
26
+
27
+ **hark** enables you to create a 'listener' object very easily. It's for programming in the *hexagonal* or *tell, don't ask* style.
28
+ The consumers of hark listeners don't know anything about hark. Because hark makes it easy to create ad-hoc object, it's easy to get
29
+ started with a tell-dont-ask style, in rails controllers for example. For more detail see the 'Rationale' section.
30
+
31
+ ## Usage
32
+
33
+ ### Create a listener
34
+
35
+ To create a listener object use `hark`.
36
+
37
+ You can pass a symbol and block
38
+
39
+ hark :created do |user|
40
+ redirect_to(user, notice: "You have signed up!")
41
+ end
42
+
43
+ The following methods are more suitable for a listener with multiple messages.
44
+
45
+ A hash with callables as values
46
+
47
+ hark(
48
+ created: ->(user) { redirect_to(user, notice: "You have signed up!") },
49
+ invalid: ->(user) { @user = user; render "new" }
50
+ )
51
+
52
+ # assuming some methods for rendering and redirecting exist on the controller
53
+ hark(created: method(:redirect_to_user), invalid: method(:render_new))
54
+
55
+ Or, a 'respond_to' style block
56
+
57
+ hark do |on|
58
+ on.created {|user| redirect_to(user, notice: "You have signed up!") }
59
+ on.invalid {|user| @user = user; render "new" }
60
+ end
61
+
62
+ ### Strict & lax listeners
63
+
64
+ By default, hark listeners are 'strict', they will only respond to the methods defined on them.
65
+
66
+ You create a 'lax' listener, responding to any message, by sending the `lax` message.
67
+
68
+ listener = hark(:foo) { "Foo" }
69
+
70
+ listener.bar
71
+ # => NoMethodError: undefined method `bar' for #<Hark::StrictListener:0x007fc91a03e568>
72
+
73
+ listener = listener.lax
74
+ listener.bar
75
+ # => []
76
+
77
+ To make a strict listener send the `strict` message.
78
+
79
+ ### Combining listeners
80
+
81
+ Here are some ways of combining listeners.
82
+
83
+ # redirect listener
84
+ listener = hark(created: method(:redirect_to_user))
85
+
86
+ Add a message
87
+
88
+ listener = listener.hark :created do |user|
89
+ WelomeMailer.send_email(user)
90
+ end
91
+
92
+ Combine with another listener
93
+
94
+ logger = listener.hark(created: ->(u) { logger.info "User #{u} created" } )
95
+ listener = listener.hark(logger)
96
+
97
+ Combine with any object that support the same protocol
98
+
99
+ logger = UserLogger.new # responds to :created
100
+ listener = listener.hark(logger)
101
+
102
+ Turn any object into a listener, adding new methods as we go
103
+
104
+ hark UserLogger.new do |on|
105
+ on.created {|user| Emailer.send_welcom_email(user) }
106
+ end
107
+
108
+ Now, when listener is sent #created, all create handlers are called.
109
+
110
+ ### Sugar: `Kernel#hearken`
111
+
112
+ Because of the precedence of the block operator, constructing ad-hoc listeners requires
113
+ you to insert some parens, which might be seen as unsightly, e.g:
114
+
115
+ seller.request_valuation(item, (hark do |on|
116
+ on.valuation_requested {|valuation| redirect_to valuation}
117
+ on.invalid_item {|item| redirect_to item, error: "Item not evaluable" }
118
+ end))
119
+
120
+ You may use Kernerl#hearken to create an ad-hoc listener using a passed block as follows
121
+
122
+ seller.hearken :request_valuation, item do |on|
123
+ on.valuation_requested {|valuation| redirect_to valuation}
124
+ on.invalid_item {|item| redirect_to item, error: "Item not evaluable" }
125
+ end
126
+
127
+ If you want to combine listeners with an ad-hoc block, you may pass a 0-arity block that is
128
+ yielded as the listener
129
+
130
+ seller.hearken :request_valuation, item do
131
+ hark valuation_notifier do |on|
132
+ on.valuation_requested {|valuation| redirect_to valuation}
133
+ on.invalid_item {|item| redirect_to item, error: "Item not evaluable" }
134
+ end
135
+ end
136
+
137
+ ### Return value
138
+
139
+ Using the return value of a listener is not encouraged. Hark is designed for a *tell, don't ask*
140
+ style of coding. That said the return value of a hark listener is an array of its handlers return values.
141
+
142
+ a = hark(:foo) { 'a' }
143
+ b = Object.new.tap {|o| o.singleton_class.send(:define_method, :foo) { 'b' } }
144
+ c = hark(foo: -> { 'c' }, bar: -> { 'c bar' })
145
+
146
+ a.foo # => ["a"]
147
+ hark(a,b).foo # => ["a", "b"]
148
+ hark(a,b,c).foo # => ["a", "b", "c"]
149
+
150
+ ### Immutable
151
+
152
+ Hark listeners are immutable and `#lax`, `#strict`, and `#hark` all return new listeners.
153
+
154
+ ## Rationale
155
+
156
+ When programming in the 'tell-dont-ask' or 'hexagonal' style, program flow is managed by passing listener, or
157
+ response, objects to service objects, which call back depending on what happened. This allows logic that is concerned with the caller's domain to remain isolated from the service object.
158
+
159
+ The idea behind **hark** is that there should be little ceremony involved in the listener/response mechanics, and
160
+ that simple listeners can easily be refactored into objects in their own right, without changing the protocols between
161
+ the calling and servcie objects.
162
+
163
+ To that end, service objects should not know anything other than the listener/response protocol, and shouldn't have to 'publish' their
164
+ results beyond a simple method call.
165
+
166
+ As a simple example, a user creation service object defines a response protocol as follows:
167
+
168
+ * created_user(user) _the user was succesfully created_
169
+ * invalid_user(user) _the user couldn't be created because it was invalid_
170
+
171
+ The UserCreator object's main method will have some code as follows:
172
+
173
+ if # some logic that means the user params were valid and we could persist the user
174
+ response.created_user(user)
175
+ else
176
+ response.invalid_user(user)
177
+ end
178
+
179
+ Let's say a controller is calling this, and you are using hark. In the beginning you would do something like this:
180
+
181
+ def create
182
+ user_creator.call(user_params, hark do |on|
183
+ on.created_user {|user| redirect_to user, notice: "Welome!" }
184
+ on.invalid_user {|user| @user = user; render "new" }
185
+ end)
186
+ end
187
+
188
+ This keeps the controller's handling of the user creation nicely separate from the saving of the user creator.
189
+
190
+ Then, a requirement comes in to log the creation of users. The first attempt might be this:
191
+
192
+ def create
193
+ user_creator.call(user_params, hark do |on|
194
+ on.created_user do |user|
195
+ redirect_to user, notice: "Welome!"
196
+ logger.info "User #{user} created"
197
+ end
198
+ on.invalid_user {|user| @user = user; render "new" }
199
+ end
200
+ end
201
+
202
+ Then a requirement comes in to email users on succesful creation, there's an UserEmailer that responds
203
+ to the same protocol. Also, the UX team want to log invalid users.
204
+
205
+ There's quite a lot going on now, we can tie it up as follows:
206
+
207
+ def create
208
+ response = hark(ui_response, UserEmailer.new, ux_team_response)
209
+ user_creator.call user_params, response
210
+ end
211
+
212
+ # UserEmailer responds to #created_user(user)
213
+
214
+ def ui_response
215
+ hark do |on|
216
+ on.created_user {|user| redirect_to user, notice: "Welome!" }
217
+ on.invalid_user {|user| @user = user; render "new" }
218
+ end
219
+ end
220
+
221
+ def ux_team_response
222
+ hark(:invalid_user) {|user| logger.info("User invalid: #{user}") }
223
+ end
224
+
225
+ If some of the response code gets hairy, we can easily swap out hark ad-hoc objects for 'proper' ones.
226
+ For example, the UI response might get a bit hairy, and so we make a new object.
227
+
228
+ def create
229
+ response = hark(UiResponse.new(self), UserEmailer.new, ux_team_response)
230
+ user_creator.call user_params, response
231
+ end
232
+
233
+ class UiResponse < SimpleDelegator
234
+ def created_user user
235
+ if request.format.json?
236
+ # ...
237
+ else
238
+ # ...
239
+ end
240
+ end
241
+
242
+ def invalid_user user
243
+ # ...
244
+ end
245
+ end
246
+
247
+ Note that throughout this process we didn't have to modify the UserCreator code, even when we transitioned
248
+ to/from hark for different repsonses/styles.
249
+
250
+ ### Testing your listeners
251
+
252
+ Don't pay any attention to hark when you're testing, hark is just a utility to create listeners, and so what
253
+ you should be testing is the protocol.
254
+
255
+ For example the service object tests will test functionality that pertains to the actual creation of the user,
256
+ and will test that the correct message is sent to the response in those circumstances. Whereas the controller tests
257
+ will mock out the service object, and test what happens when the service object sends the messages to the response as
258
+ dictated by the protocol.
259
+
260
+ describe UserCreator do
261
+ let(:service) { described_class.new }
262
+
263
+ describe "#call params, response" do
264
+ subject { service.call params, response }
265
+
266
+ let(:response) { double }
267
+
268
+ context "when the user succesfully saves"
269
+ let(:params) { {name: "created user", # and other successful user params }
270
+
271
+ it "sends #created_user to the response with the created user" do
272
+ response.should_receive(:created_user) do |user|
273
+ user.name.should == "created user"
274
+ end
275
+ subject
276
+ end
277
+ end
278
+
279
+ context "when the user succesfully saves"
280
+ let(:params) { {name: "invalid user", # and invalid user params }
281
+
282
+ it "sends #invalid_user to the response with the created user" do
283
+ response.should_receive(:invalid_user) do |user|
284
+ # test that the object passed is the invalid user
285
+ end
286
+ subject
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ describe NewUserController do
293
+ before { controller.stub(user_creator: user_creator) } # or some other sensible way of injecting a fake user_creator
294
+
295
+ let(:user_creator) { double "User creator" }
296
+ let(:user) { double "A user" }
297
+
298
+ context "when the user_creator is succesful" do
299
+ before do
300
+ user_creator.stub :call do |params, response|
301
+ response.created_user(user)
302
+ end
303
+ end
304
+
305
+ it "should redirect to the user"
306
+
307
+ it "should email the user"
308
+
309
+ it "should log the creation of the user"
310
+ end
311
+
312
+ context "when the user_creator says the params are invalid" do
313
+ before do
314
+ user_creator.stub :call do |params, response|
315
+ response.invalid_user(user)
316
+ end
317
+ end
318
+
319
+ it "should render new with the user"
320
+
321
+ it "should log something for the UX team"
322
+ end
323
+ end
324
+
325
+
326
+ ## Contributing
327
+
328
+ 1. Fork it
329
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
330
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
331
+ 4. Push to the branch (`git push origin my-new-feature`)
332
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/hark.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hark/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "heed"
8
+ spec.version = Hark::VERSION
9
+ spec.authors = ["Ian White"]
10
+ spec.email = ["ian.w.white@gmail.com"]
11
+ spec.description = %q{Create ad-hoc listener objects with impunity}
12
+ spec.summary = %q{Hark is a gem that enables writing code in a "hexagonal architecture" or "tell don't ask" style}
13
+ spec.homepage = "http://github.com/ianwhite/hark"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ if RUBY_VERSION > "1.9"
26
+ spec.add_development_dependency "coveralls"
27
+ end
28
+ end
data/lib/hark.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "hark/version"
2
+ require "hark/ad_hoc"
3
+ require "hark/dispatcher"
4
+ require "hark/listener"
5
+ require "hark/core_ext"
6
+
7
+ module Hark
8
+ def self.from(*args, &block)
9
+ Listener.new(*args, &block)
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ module Hark
2
+ # AdHoc is a tiny class to facilitate creating an ad-hoc object that from either a hash or proc.
3
+ #
4
+ # Eg. from a hash:
5
+ #
6
+ # handler = AdHoc.new(success: (o)-> { o.great_success }, failure: (o)-> { o.failed } )
7
+ #
8
+ # Eg. from a 'response' style block:
9
+ #
10
+ # handler = AdHoc.new do |on|
11
+ # on.success {|o| o.great_success }
12
+ # on.failure {|o| o.failed }
13
+ # end
14
+ #
15
+ # Eg. adding methods after creation
16
+ #
17
+ # obj = AdHoc.new
18
+ # obj.add_method!(:foo) { "bar" }
19
+ #
20
+ # All blocks keep their original binding. This makes AdHoc suitable for creating
21
+ # ad-hoc responses from controller type objects.
22
+ #
23
+ class AdHoc
24
+ def self.new hash = {}, &proc
25
+ super().tap do |ad_hoc|
26
+ AddMethodsFromProc.new(proc, ad_hoc) if block_given?
27
+ hash.each {|method, body| ad_hoc.add_method!(method, &body) }
28
+ end
29
+ end
30
+
31
+ def add_method!(method, &body)
32
+ singleton_class = class << self; self; end
33
+ singleton_class.send(:define_method, method) {|*args, &block| body.call(*args, &block) }
34
+ end
35
+
36
+ class AddMethodsFromProc
37
+ def initialize proc, ad_hoc
38
+ @ad_hoc = ad_hoc
39
+ proc.call(self)
40
+ end
41
+
42
+ def method_missing method, *, &body
43
+ @ad_hoc.add_method!(method, &body)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,10 @@
1
+ module Kernel
2
+ def hark *args, &block
3
+ Hark.from *args, &block
4
+ end
5
+
6
+ def hearken method, *args, &block
7
+ listener = (block.arity == 1) ? hark(&block) : block.call
8
+ send method, *args + [listener]
9
+ end
10
+ end
@@ -0,0 +1,50 @@
1
+ module Hark
2
+ class Dispatcher
3
+ # from(:success) do
4
+ # "success"
5
+ # end
6
+ #
7
+ # from(success: ->{ "success" })
8
+ #
9
+ # from do |on|
10
+ # on.success { "success" }
11
+ # end
12
+ #
13
+ def self.from(*args, &block)
14
+ if block
15
+ args << (args.last.is_a?(Symbol) ? {args.pop => block} : block)
16
+ end
17
+
18
+ new args.map{|o| to_handler(o) }.flatten.freeze
19
+ end
20
+
21
+ def self.to_handler object
22
+ case object
23
+ when Listener then object.dispatcher.handlers
24
+ when Dispatcher then object.handlers
25
+ when Hash then AdHoc.new(object)
26
+ when Proc then AdHoc.new(&object)
27
+ else object
28
+ end
29
+ end
30
+
31
+ attr_reader :handlers
32
+
33
+ def initialize handlers
34
+ @handlers = handlers
35
+ freeze
36
+ end
37
+
38
+ def handles? method
39
+ handlers.any? {|handler| handler.respond_to?(method) }
40
+ end
41
+
42
+ def handle method, *args, &block
43
+ results = []
44
+ handlers.each do |handler|
45
+ results << handler.send(method, *args, &block) if handler.respond_to?(method)
46
+ end
47
+ results
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,58 @@
1
+ module Hark
2
+ # A Listener holds a dispatcher, which it dispatches messages to
3
+ #
4
+ # A listener is by default a 'strict' listener, it will raise NoMethodError if
5
+ # it is sent a message it doesn't know how to handle.
6
+ #
7
+ # A listener can be turned into a 'lax' listener, by sending it the #lax message.
8
+ # A lax listener will silently swallow any unknown messages.
9
+ class Listener
10
+ def self.new *args, &block
11
+ self == Listener ? StrictListener.new(*args, &block) : super(*args, &block)
12
+ end
13
+
14
+ attr_reader :dispatcher
15
+
16
+ def initialize(*args, &block)
17
+ @dispatcher = Dispatcher.from(*args, &block)
18
+ freeze
19
+ end
20
+
21
+ def strict
22
+ StrictListener.new dispatcher
23
+ end
24
+
25
+ def lax
26
+ LaxListener.new dispatcher
27
+ end
28
+
29
+ def hark *args, &block
30
+ self.class.new dispatcher, *args, &block
31
+ end
32
+ end
33
+
34
+ class StrictListener < Listener
35
+ def respond_to?(method, *args)
36
+ super || dispatcher.handles?(method)
37
+ end
38
+
39
+ def method_missing *args, &block
40
+ results = dispatcher.handle(*args, &block)
41
+ if results.length > 0
42
+ results
43
+ else
44
+ super
45
+ end
46
+ end
47
+ end
48
+
49
+ class LaxListener < Listener
50
+ def respond_to? *args
51
+ true
52
+ end
53
+
54
+ def method_missing *args, &block
55
+ dispatcher.handle(*args, &block)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module Hark
2
+ VERSION = "0.0.7"
3
+ end
data/spec/hark_spec.rb ADDED
@@ -0,0 +1,172 @@
1
+ require 'spec_helper'
2
+ require 'hark'
3
+
4
+ describe Hark do
5
+ let(:transcript) { [] }
6
+
7
+ class PlainListener < Struct.new(:transcript)
8
+ def success(value)
9
+ transcript.push [:succeeded, value]
10
+ end
11
+
12
+ def failure(value)
13
+ transcript.push [:failed, value]
14
+ end
15
+ end
16
+
17
+ shared_examples_for "a success/failure listener" do
18
+ describe "success" do
19
+ before { listener.success(42) }
20
+ it { transcript.should == [[:succeeded, 42]] }
21
+ end
22
+
23
+ describe "failure" do
24
+ before { listener.failure(54) }
25
+ it { transcript.should == [[:failed, 54]] }
26
+ end
27
+ end
28
+
29
+ shared_examples_for "a strict listener" do
30
+ it { strict_listener.should_not respond_to(:unknown) }
31
+ it { expect{ strict_listener.unknown }.to raise_error(NoMethodError) }
32
+ end
33
+
34
+ shared_examples_for "a lax listener" do
35
+ it { lax_listener.should respond_to(:unknown) }
36
+ it { lax_listener.unknown.should == [] }
37
+ end
38
+
39
+ shared_examples_for "a success/failure hark listener" do
40
+ it_should_behave_like "a success/failure listener"
41
+ it_should_behave_like "a strict listener" do
42
+ let(:strict_listener) { listener }
43
+ end
44
+
45
+ context "when made lax" do
46
+ let(:lax_listener) { listener.lax }
47
+ it_should_behave_like "a lax listener"
48
+
49
+ context "and made strict again" do
50
+ let(:strict_listener) { lax_listener.strict }
51
+ it_should_behave_like "a strict listener"
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "A plain (non hark) listener object" do
57
+ let(:listener) { PlainListener.new(transcript) }
58
+
59
+ it_should_behave_like "a success/failure listener"
60
+ it_should_behave_like "a strict listener" do
61
+ let(:strict_listener) { listener }
62
+ end
63
+ end
64
+
65
+ describe "hark with respond_to style block" do
66
+ let(:listener) do
67
+ hark do |on|
68
+ on.success {|v| transcript.push [:succeeded, v] }
69
+ on.failure {|v| transcript.push [:failed, v] }
70
+ end
71
+ end
72
+
73
+ it_should_behave_like "a success/failure hark listener"
74
+ end
75
+
76
+ describe "hark with callables" do
77
+ let(:listener) do
78
+ hark :success => lambda{|v| transcript.push [:succeeded, v] }, :failure => lambda{|v| transcript.push [:failed, v] }
79
+ end
80
+
81
+ it_should_behave_like "a success/failure hark listener"
82
+ end
83
+
84
+ describe "hark built up in steps" do
85
+ let(:listener) do
86
+ l = hark
87
+ l = l.hark(:success) {|v| transcript.push [:succeeded, v] }
88
+ l = l.hark(:failure) {|v| transcript.push [:failed, v] }
89
+ end
90
+
91
+ it_should_behave_like "a success/failure hark listener"
92
+ end
93
+
94
+ describe "#hark(object)" do
95
+ let(:listener) { hark PlainListener.new(transcript) }
96
+
97
+ it_should_behave_like "a success/failure hark listener"
98
+ end
99
+
100
+ describe "combine two listeners together" do
101
+ let(:logger) { hark(:signup_user) {|user| transcript << "User #{user} signed up" } }
102
+ let(:emailer) { hark(:signup_user) {|user| transcript << "Emailed #{user}" } }
103
+
104
+ shared_examples_for "combined listeners" do
105
+ before { listener.signup_user("Fred") }
106
+
107
+ it { transcript.should == ["User Fred signed up", "Emailed Fred"] }
108
+ end
109
+
110
+ it_behaves_like "combined listeners" do
111
+ let(:listener) { logger.hark(emailer) }
112
+ end
113
+
114
+ it_behaves_like "combined listeners" do
115
+ let(:listener) { hark(logger, emailer) }
116
+ end
117
+
118
+ it_behaves_like "combined listeners" do
119
+ let(:listener) do
120
+ hark logger do |on|
121
+ on.signup_user {|user| transcript << "Emailed #{user}" }
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ describe "lax/strict is preserved on #hark" do
128
+ it { hark.lax.hark.should be_a Hark::LaxListener }
129
+ it { hark.strict.hark.should be_a Hark::StrictListener }
130
+ end
131
+
132
+ describe "when methods return falsy" do
133
+ let(:listener) { hark(:foo) { false } }
134
+
135
+ it { expect{ listener.foo }.to_not raise_error }
136
+ it { listener.foo.should == [false] }
137
+ end
138
+
139
+ describe "#hearken :method" do
140
+ let(:object) do
141
+ Object.new.tap do |obj|
142
+ class << obj
143
+ def foo arg1, arg2, listener
144
+ listener.foo(arg1)
145
+ listener.bar(arg2)
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ context "with 1 arity block" do
152
+ it "sends :method with an ad-hoc listener created from the block" do
153
+ object.hearken :foo, "ONE", "TWO" do |on|
154
+ on.foo {|a| transcript << [:foo, a] }
155
+ on.bar {|a| transcript << [:bar, a] }
156
+ end
157
+ transcript.should == [[:foo, "ONE"], [:bar, "TWO"]]
158
+ end
159
+ end
160
+
161
+ context "with 0 arity block" do
162
+ it "sends :method with listener created by yielding to the block" do
163
+ foo = hark(:foo) {|a| transcript << [:foo, a] }
164
+
165
+ object.hearken :foo, "ONE", "TWO" do
166
+ hark(foo, :bar) {|a| transcript << [:bar, a] }
167
+ end
168
+ transcript.should == [[:foo, "ONE"], [:bar, "TWO"]]
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.dirname('../lib')
2
+
3
+ require 'rspec'
4
+
5
+ begin
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+ rescue LoadError
9
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Ian White
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Create ad-hoc listener objects with impunity
70
+ email:
71
+ - ian.w.white@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .coveralls.yml
77
+ - .gitignore
78
+ - .travis.yml
79
+ - Gemfile
80
+ - History.md
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - hark.gemspec
85
+ - lib/hark.rb
86
+ - lib/hark/ad_hoc.rb
87
+ - lib/hark/core_ext.rb
88
+ - lib/hark/dispatcher.rb
89
+ - lib/hark/listener.rb
90
+ - lib/hark/version.rb
91
+ - spec/hark_spec.rb
92
+ - spec/spec_helper.rb
93
+ homepage: http://github.com/ianwhite/hark
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.0.6
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Hark is a gem that enables writing code in a "hexagonal architecture" or
117
+ "tell don't ask" style
118
+ test_files:
119
+ - spec/hark_spec.rb
120
+ - spec/spec_helper.rb