poniard 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Poniard History
2
2
 
3
+ ## 0.0.2 - 26 January 2013
4
+
5
+ * Hashes can be used directly as sources.
6
+ * Provide default argument for `response.default`.
7
+
3
8
  ## 0.0.1 - 25 November 2012
4
9
 
5
- * Initial release
10
+ * Initial release.
data/README.md CHANGED
@@ -24,13 +24,15 @@ normally deal with in Rails (`session`, `flash`, etc...).
24
24
  The following controller renders the default index template, setting the
25
25
  instance variables `@message`.
26
26
 
27
- module Controller
28
- class Registration
29
- def index(response)
30
- response.default message: "hello"
31
- end
32
- end
27
+ ```ruby
28
+ module Controller
29
+ class Registration
30
+ def index(response)
31
+ response.default message: "hello"
33
32
  end
33
+ end
34
+ end
35
+ ```
34
36
 
35
37
  This is more explicit than traditional controllers in two ways: passing
36
38
  variables to the template is done with an explicit method call rather than
@@ -40,30 +42,34 @@ available by a superclass are passed in as parameters to the method.
40
42
  Wiring this controller into your application is a one-liner in your normal
41
43
  controller definition.
42
44
 
43
- class RegistrationsController < ApplicationController
44
- include Poniard::Controller
45
+ ```ruby
46
+ class RegistrationsController < ApplicationController
47
+ include Poniard::Controller
45
48
 
46
- provided_by Controller::Registration
47
- end
49
+ provided_by Controller::Registration
50
+ end
51
+ ```
48
52
 
49
53
  You can mix and match traditional and poniard styles. Some actions can be
50
54
  implemented in the normal controller, others can be provided by an injectable
51
55
  one.
52
56
 
53
- class RegistrationsController < ApplicationController
54
- include Poniard::Controller
57
+ ```ruby
58
+ class RegistrationsController < ApplicationController
59
+ include Poniard::Controller
55
60
 
56
- # index action provided by this class
57
- provided_by Controller::Registration
61
+ # index action provided by this class
62
+ provided_by Controller::Registration
58
63
 
59
- # All controller features work in harmony with poniard, such as this
60
- before_filter :require_user
64
+ # All controller features work in harmony with poniard, such as this
65
+ before_filter :require_user
61
66
 
62
- # update action implemented normally
63
- def update
64
- # ...
65
- end
66
- end
67
+ # update action implemented normally
68
+ def update
69
+ # ...
70
+ end
71
+ end
72
+ ```
67
73
 
68
74
  ### Sources
69
75
 
@@ -71,23 +77,27 @@ Poniard knows about all the standard controller objects such as `response`,
71
77
  `session` and `flash`. You then layer your own domain specific definitions on
72
78
  top by creating **sources**:
73
79
 
74
- class Source
75
- class Registration
76
- def finder
77
- Registration.accepted
78
- end
80
+ ```ruby
81
+ class Source
82
+ class Registration
83
+ def finder
84
+ Registration.accepted
85
+ end
79
86
 
80
- def current_registration
81
- Registration.find(params[:id])
82
- end
83
- end
87
+ def current_registration
88
+ Registration.find(params[:id])
84
89
  end
90
+ end
91
+ end
92
+ ```
85
93
 
86
94
  Wire this up in the `provided_by` call:
87
95
 
88
- provided_by Controller::Registration, sources: [
89
- Source::Registration
90
- ]
96
+ ```ruby
97
+ provided_by Controller::Registration, sources: [
98
+ Source::Registration
99
+ ]
100
+ ```
91
101
 
92
102
  You can specify as many sources as you like, making it easy to reuse logic
93
103
  across controllers.
@@ -99,28 +109,30 @@ Set up a common injector for the scope of your controller that knows about
99
109
  common sources that all tests require (such as `response`). Add extra required
100
110
  sources on a per test basis (`finder` in the below example).
101
111
 
102
- require 'poniard/injector'
103
- require 'controller/registration'
112
+ ```ruby
113
+ require 'poniard/injector'
114
+ require 'controller/registration'
104
115
 
105
- describe Controller::Registration do
106
- let(:response) { double("Poniard::ControllerSource") }
107
- let(:injector) { Poniard::Injector.new([OpenStruct.new(
108
- response: response.as_null_object
109
- )]) }
116
+ describe Controller::Registration do
117
+ let(:response) { double("Poniard::ControllerSource") }
118
+ let(:injector) { Poniard::Injector.new([
119
+ response: response.as_null_object
120
+ ]) }
110
121
 
111
- def dispatch(action, overrides = {})
112
- injector.dispatch described_class.new.method(action), overrides
113
- end
122
+ def dispatch(action, overrides = {})
123
+ injector.dispatch described_class.new.method(action), overrides
124
+ end
114
125
 
115
- describe '#index' do
116
- it 'should render default action with all registrations' do
117
- finder = double(all: ['r1'])
118
- response.should_receive(:default).with(registrations: ['r1'])
126
+ describe '#index' do
127
+ it 'should render default action with all registrations' do
128
+ finder = double(all: ['r1'])
129
+ response.should_receive(:default).with(registrations: ['r1'])
119
130
 
120
- dispatch :index, finder: finder
121
- end
122
- end
131
+ dispatch :index, finder: finder
123
132
  end
133
+ end
134
+ end
135
+ ```
124
136
 
125
137
  Techniques
126
138
  ----------
@@ -142,58 +154,64 @@ The Rails `respond_with` API is not very OO, so is hard to test in isolation.
142
154
  Poniard provides a wrapper that allows you to provide a response object that is
143
155
  much easier to work with.
144
156
 
145
- module Controller
146
- class Registration
147
- def index(response, finder)
148
- response.respond_with RegistrationsIndexResponse, finder.all
149
- end
150
-
151
- RegistrationsIndexResponse = Struct.new(:registrations) do
152
- def html(response)
153
- response.default registrations: registrations
154
- end
155
-
156
- def json(response)
157
- response.render json: registrations.to_json
158
- end
159
- end
157
+ ```ruby
158
+ module Controller
159
+ class Registration
160
+ def index(response, finder)
161
+ response.respond_with RegistrationsIndexResponse, finder.all
162
+ end
163
+
164
+ RegistrationsIndexResponse = Struct.new(:registrations) do
165
+ def html(response)
166
+ response.default registrations: registrations
167
+ end
168
+
169
+ def json(response)
170
+ response.render json: registrations.to_json
160
171
  end
161
172
  end
173
+ end
174
+ end
175
+ ```
162
176
 
163
177
  ### Authorization
164
178
 
165
179
  Poniard sources can raise exceptions to indicate authorization failure, which
166
180
  can then be handled in a standard manner using `rescue_from`.
167
181
 
168
- module Source
169
- class Admin
170
- def current_organiser(session)
171
- Organiser.find_by_id(session[:organiser_id])
172
- end
182
+ ```ruby
183
+ module Source
184
+ class Admin
185
+ def current_organiser(session)
186
+ Organiser.find_by_id(session[:organiser_id])
187
+ end
173
188
 
174
- def authorized_organiser(current_organiser)
175
- current_organiser || raise(ResponseException::Unauthorized)
176
- end
177
- end
189
+ def authorized_organiser(current_organiser)
190
+ current_organiser || raise(ResponseException::Unauthorized)
178
191
  end
192
+ end
193
+ end
194
+ ```
179
195
 
180
196
  This can be slightly weird if the method you are authorizing does not actually
181
197
  need to interact with the organiser, since it will have a method parameter that
182
198
  is never used.
183
199
 
184
- RSpec::Matchers.define :have_param do |attribute|
185
- match do |obj|
186
- obj.parameters.map(&:last).include?(attribute)
187
- end
188
- end
200
+ ```ruby
201
+ RSpec::Matchers.define :have_param do |attribute|
202
+ match do |obj|
203
+ obj.parameters.map(&:last).include?(attribute)
204
+ end
205
+ end
189
206
 
190
- def instance_method(name)
191
- described_class.new.method(name)
192
- end
207
+ def instance_method(name)
208
+ described_class.new.method(name)
209
+ end
193
210
 
194
- it 'requires authorization' do
195
- instance_method(:index).should have_param(:authorized_organiser)
196
- end
211
+ it 'requires authorization' do
212
+ instance_method(:index).should have_param(:authorized_organiser)
213
+ end
214
+ ```
197
215
 
198
216
  Developing
199
217
  ----------
@@ -210,11 +228,11 @@ Requires 1.9, should be easy to backport to 1.8 if anyone is interested. Use
210
228
  1.9 style hashes and probably relies on `methods` calls returning symbols
211
229
  rather than strings.
212
230
 
213
- ## Support
231
+ ### Support
214
232
 
215
233
  Make a [new github issue](https://github.com/xaviershay/poniard/issues/new).
216
234
 
217
- ## Contributing
235
+ ### Contributing
218
236
 
219
237
  Fork and patch! Please update the README and other documentation if you add
220
238
  features.
@@ -37,7 +37,7 @@ module Poniard
37
37
  controller.render *args
38
38
  end
39
39
 
40
- def default(ivars)
40
+ def default(ivars = {})
41
41
  ivars.each do |name, val|
42
42
  controller.instance_variable_set("@#{name}", val)
43
43
  end
@@ -4,28 +4,40 @@ module Poniard
4
4
  class Injector
5
5
  attr_reader :sources
6
6
 
7
- def initialize(sources)
8
- @sources = sources + [self]
7
+ def initialize(sources = [])
8
+ @sources = sources.map {|source|
9
+ if source.is_a? Hash
10
+ OpenStruct.new(source)
11
+ else
12
+ source
13
+ end
14
+ }+ [self]
9
15
  end
10
16
 
11
17
  def dispatch(method, overrides = {})
12
18
  args = method.parameters.map {|_, name|
13
- source = (
14
- [OpenStruct.new(overrides)] +
15
- sources
16
- ).detect {|source| source.respond_to?(name) }
19
+ source = sources_for(overrides).detect {|source|
20
+ source.respond_to?(name)
21
+ }
22
+
17
23
  if source
18
24
  dispatch(source.method(name), overrides)
19
25
  else
20
26
  UnknownInjectable.new(name)
21
27
  end
22
28
  }
23
- method.call(*args)
29
+ method.(*args)
24
30
  end
25
31
 
26
32
  def injector
27
33
  self
28
34
  end
35
+
36
+ private
37
+
38
+ def sources_for(overrides)
39
+ [OpenStruct.new(overrides)] + sources
40
+ end
29
41
  end
30
42
 
31
43
  class UnknownParam < RuntimeError; end
@@ -1,3 +1,3 @@
1
1
  module Poniard
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -1,15 +1,83 @@
1
1
  require 'poniard/injector'
2
2
 
3
3
  describe Poniard::Injector do
4
+ let(:thing) { Object.new }
5
+
6
+ it 'calls a lambda' do
7
+ called = false
8
+ described_class.new([]).dispatch -> { called = true }
9
+ called.should == true
10
+ end
11
+
12
+ it 'calls a method' do
13
+ object = Class.new do
14
+ def a_number
15
+ 12345
16
+ end
17
+ end.new
18
+
19
+ described_class.new([]).dispatch(object.method(:a_number)).should == 12345
20
+ end
21
+
22
+ it 'calls a method with a parameter provided by a source' do
23
+ called = false
24
+ described_class.new([
25
+ thing: thing
26
+ ]).dispatch ->(thing) { called = thing }
27
+ called.should == thing
28
+ end
29
+
30
+ it 'recursively injects sources' do
31
+ two_source = Class.new do
32
+ def two_things(thing)
33
+ [thing, thing]
34
+ end
35
+ end.new
36
+
37
+ called = false
38
+ described_class.new([
39
+ {thing: thing},
40
+ two_source
41
+ ]).dispatch ->(two_things) { called = two_things }
42
+ called.should == [thing, thing]
43
+ end
44
+
45
+ it 'uses the first source that provides a parameter' do
46
+ called = false
47
+ described_class.new([
48
+ {thing: thing},
49
+ {thing: nil}
50
+ ]).dispatch ->(thing) { called = thing }
51
+ called.should == thing
52
+ end
53
+
54
+ it 'allows sources to be overriden at dispatch' do
55
+ called = false
56
+ described_class.new([
57
+ {thing: nil}
58
+ ]).dispatch ->(thing) { called = thing }, thing: thing
59
+ called.should == thing
60
+ end
61
+
62
+ it 'provides itself as a source' do
63
+ called = false
64
+ injector = described_class.new
65
+ injector.dispatch ->(injector) { called = injector }
66
+ called.should == injector
67
+ end
68
+
4
69
  it 'yields a fail object when source is unknown' do
5
70
  called = false
6
71
  m = ->(unknown) {
7
72
  ->{
8
73
  unknown.bogus
9
- }.should raise_error(Poniard::UnknownParam)
74
+ }.should raise_error(
75
+ Poniard::UnknownParam,
76
+ "Tried to call method on an uninjected param: unknown"
77
+ )
10
78
  called = true
11
79
  }
12
- Poniard::Injector.new([]).dispatch(m)
80
+ described_class.new.dispatch(m)
13
81
  called.should be_true
14
82
  end
15
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poniard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-25 00:00:00.000000000 Z
12
+ date: 2013-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec