objectify 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +54 -2
- data/lib/objectify.rb +0 -3
- data/lib/objectify/config/context.rb +37 -22
- data/lib/objectify/config/injectables.rb +36 -0
- data/lib/objectify/injector.rb +37 -9
- data/lib/objectify/rails/controller.rb +15 -15
- data/lib/objectify/rails/routing.rb +10 -2
- data/lib/objectify/version.rb +1 -1
- data/spec/config/context_spec.rb +0 -13
- data/spec/config/injectables_spec.rb +40 -0
- data/spec/injector_spec.rb +48 -34
- data/spec/rails/routing_spec.rb +19 -6
- data/thoughts/injector.md +76 -0
- metadata +20 -17
data/README.md
CHANGED
@@ -86,7 +86,7 @@ Objectify has two primary components:
|
|
86
86
|
end
|
87
87
|
```
|
88
88
|
|
89
|
-
2. A dependency injection framework. Objectify automatically injects dependencies into objects it manages based on parameter names. So, if you have a service method signature like `PictureCreationService#call(params)`, objectify will automatically inject the request's params when it calls that method. It's very simple to create custom injections
|
89
|
+
2. A dependency injection framework. Objectify automatically injects dependencies into objects it manages based on parameter names. So, if you have a service method signature like `PictureCreationService#call(params)`, objectify will automatically inject the request's params when it calls that method. It's very simple to create custom injections. More on that below.
|
90
90
|
|
91
91
|
|
92
92
|
## What if I have a bunch of existing rails code?
|
@@ -140,7 +140,47 @@ end
|
|
140
140
|
|
141
141
|
## Custom Injections
|
142
142
|
|
143
|
-
|
143
|
+
There are a few ways to customize what gets injected when. By default, when objectify sees a parameter called `something`, it'll first look to see if something is specifically configured for that name, then it'll attempt to satisfy it by calling `Something.new`. If that doesn't exist, it'll try `SomethingResolver.new`, which it'll then call `#call` on. If that doesn't exist, it'll raise an error.
|
144
|
+
|
145
|
+
You can configure the injector in 3 ways. The first is used to specify an implemenation.
|
146
|
+
|
147
|
+
Let's say you had a PictureCreationService whose constructor took a parameter called `storage`.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class PictureCreationService
|
151
|
+
def initialize(storage)
|
152
|
+
@storage = storage
|
153
|
+
end
|
154
|
+
|
155
|
+
# ... more code ...
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
You can tell the injector what to supply for that parameter like this:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
objectify.implementations :storage => :s3_storage
|
163
|
+
```
|
164
|
+
|
165
|
+
Another option is to specify a value. For example, you might have a service class with a page_size parameter.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class PicturesIndexService
|
169
|
+
def initialize(page_size)
|
170
|
+
@page_size = page_size
|
171
|
+
end
|
172
|
+
|
173
|
+
# ... more code ...
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
You can tell the injector what size to make the pages like this:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
objectify.values :page_size => 20
|
181
|
+
```
|
182
|
+
|
183
|
+
Finally, you can tell objectify about `resolvers`. Resolvers are objects that know how to fulfill parameters. For example, several of the above methods have parameters named `current_user`. Here's how to create a custom resolver for it that'll automatically get found by name.
|
144
184
|
|
145
185
|
```ruby
|
146
186
|
# app/resolvers/current_user_resolver.rb
|
@@ -156,6 +196,18 @@ class CurrentUserResolver
|
|
156
196
|
end
|
157
197
|
```
|
158
198
|
|
199
|
+
If you wanted to explicitly configure that resolver, you'd do it like this:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
objectify.resolvers :current_user => :current_user
|
203
|
+
```
|
204
|
+
|
205
|
+
If that resolver was in the namespace ObjectifyAuth, you'd configure it like this:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
objectify.resolvers :current_user => "objectify_auth/current_user"
|
209
|
+
```
|
210
|
+
|
159
211
|
### Why did you constructor-inject the User constant in to the CurrentUserResolver?
|
160
212
|
|
161
213
|
Because that makes it possible to test in isolation.
|
data/lib/objectify.rb
CHANGED
@@ -1,35 +1,36 @@
|
|
1
1
|
require "objectify/config/action"
|
2
|
+
require "objectify/config/injectables"
|
2
3
|
require "objectify/config/policies"
|
3
4
|
require "objectify/injector"
|
4
5
|
require "objectify/instantiator"
|
5
|
-
require "objectify/resolver"
|
6
|
-
require "objectify/resolver_locator"
|
7
6
|
require "objectify/executor"
|
8
7
|
|
9
8
|
module Objectify
|
10
9
|
module Config
|
11
10
|
class Context
|
12
|
-
DONT_RELOAD = [:@objectify_controller
|
11
|
+
DONT_RELOAD = [:@objectify_controller,
|
12
|
+
:@policies_factory,
|
13
|
+
:@action_factory].freeze
|
13
14
|
|
14
15
|
attr_reader :policy_responders, :defaults, :actions, :policies
|
15
|
-
attr_writer :injector, :
|
16
|
-
:
|
16
|
+
attr_writer :injector, :instantiator, :executor,
|
17
|
+
:injectables, :objectify_controller
|
17
18
|
|
18
19
|
def initialize(policies_factory = Policies, action_factory = Action)
|
19
20
|
@policies_factory = policies_factory
|
20
21
|
@action_factory = action_factory
|
22
|
+
end
|
21
23
|
|
22
|
-
|
23
|
-
@
|
24
|
-
@actions = {}
|
24
|
+
def policy_responders
|
25
|
+
@policy_responders ||= {}
|
25
26
|
end
|
26
27
|
|
27
28
|
def append_policy_responders(responders)
|
28
|
-
|
29
|
+
policy_responders.merge!(responders)
|
29
30
|
end
|
30
31
|
|
31
32
|
def policy_responder(policy)
|
32
|
-
|
33
|
+
policy_responders[policy] ||
|
33
34
|
raise(ArgumentError, "Can't find a responder for #{policy}.")
|
34
35
|
end
|
35
36
|
|
@@ -41,38 +42,52 @@ module Objectify
|
|
41
42
|
@policies = @policies_factory.new(defaults)
|
42
43
|
end
|
43
44
|
|
45
|
+
def actions
|
46
|
+
@actions ||= {}
|
47
|
+
end
|
48
|
+
|
44
49
|
def append_action(action)
|
45
|
-
|
50
|
+
actions[action.route] = action
|
46
51
|
end
|
47
52
|
|
48
53
|
def action(route)
|
49
|
-
|
54
|
+
actions[route] ||
|
50
55
|
raise(ArgumentError, "No action matching #{route} was found.")
|
51
56
|
end
|
52
57
|
|
53
58
|
def legacy_action(route)
|
54
|
-
|
59
|
+
actions[route] ||
|
55
60
|
@action_factory.new(route.resource, route.action, {}, policies)
|
56
61
|
end
|
57
62
|
|
58
63
|
def injector
|
59
|
-
@injector ||= Injector.new(
|
64
|
+
@injector ||= Injector.new(injectables)
|
60
65
|
end
|
61
66
|
|
62
|
-
def
|
67
|
+
def injectables
|
68
|
+
@injectables ||= Injectables.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def append_values(opts)
|
63
72
|
opts.each do |k,v|
|
64
|
-
|
73
|
+
injectables.add_value(k, v)
|
65
74
|
end
|
66
75
|
end
|
67
76
|
|
68
|
-
def
|
69
|
-
|
77
|
+
def append_implementations(opts)
|
78
|
+
opts.each do |k,v|
|
79
|
+
injectables.add_implementation(k, v)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def append_resolvers(opts)
|
84
|
+
opts.each do |k,v|
|
85
|
+
injectables.add_resolver(k, v)
|
86
|
+
end
|
70
87
|
end
|
71
88
|
|
72
|
-
def
|
73
|
-
@
|
74
|
-
[locator, ConstResolverLocator.new]
|
75
|
-
)
|
89
|
+
def resolvers
|
90
|
+
@resolvers ||= NamedValueResolverLocator.new(NameTranslationResolver)
|
76
91
|
end
|
77
92
|
|
78
93
|
def instantiator
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Objectify
|
2
|
+
module Config
|
3
|
+
class Injectables
|
4
|
+
attr_reader :config
|
5
|
+
attr_writer :context
|
6
|
+
|
7
|
+
def initialize(config = {})
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_resolver(name, value)
|
12
|
+
@config[name] = [:resolver, value]
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_value(name, value)
|
16
|
+
@config[name] = [:value, value]
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_implementation(name, value)
|
20
|
+
@config[name] = [:implementation, value]
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(name)
|
24
|
+
@config[name] || context[name] || [:unknown, name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def context
|
28
|
+
@context && @context.config || {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def merge(other)
|
32
|
+
self.class.new @config.merge(other.config)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/objectify/injector.rb
CHANGED
@@ -4,24 +4,40 @@ module Objectify
|
|
4
4
|
class Injector
|
5
5
|
include Instrumentation
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
attr_writer :config
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
9
11
|
end
|
10
12
|
|
11
13
|
def call(object, method)
|
12
14
|
payload = {:object => object, :method => method}
|
13
15
|
instrument("inject.objectify", payload) do |payload|
|
14
16
|
method_obj = method_object(object, method)
|
15
|
-
|
16
|
-
@
|
17
|
+
injectables = method_obj.parameters.map do |reqd, name|
|
18
|
+
@config.get(name) if reqd == :req
|
17
19
|
end.compact
|
18
|
-
arguments =
|
19
|
-
|
20
|
+
arguments = injectables.map do |type, value|
|
21
|
+
if type == :unknown
|
22
|
+
type, value = unknown_value_to_injectable(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
if type == :resolver
|
26
|
+
resolver_klass = [value, :resolver].join("_").classify.constantize
|
27
|
+
call(call(resolver_klass, :new), :call)
|
28
|
+
elsif type == :implementation
|
29
|
+
implementation_klass = value.to_s.classify.constantize
|
30
|
+
call(implementation_klass, :new)
|
31
|
+
elsif type == :value
|
32
|
+
value
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Unknown injectable type: #{type}."
|
35
|
+
end
|
20
36
|
end
|
21
37
|
|
22
|
-
payload[:parameters]
|
23
|
-
payload[:
|
24
|
-
payload[:arguments]
|
38
|
+
payload[:parameters] = method_obj.parameters
|
39
|
+
payload[:injectables] = injectables
|
40
|
+
payload[:arguments] = arguments
|
25
41
|
|
26
42
|
object.send(method, *arguments)
|
27
43
|
end
|
@@ -35,5 +51,17 @@ module Objectify
|
|
35
51
|
object.method(method)
|
36
52
|
end
|
37
53
|
end
|
54
|
+
|
55
|
+
def unknown_value_to_injectable(value)
|
56
|
+
[nil, :resolver].each do |suffix|
|
57
|
+
begin
|
58
|
+
[value, suffix].compact.join("_").classify.constantize
|
59
|
+
return [suffix.nil? ? :implementation : suffix, value]
|
60
|
+
rescue NameError => e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
raise ArgumentError, "Can't figure out how to inject #{value}."
|
65
|
+
end
|
38
66
|
end
|
39
67
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "objectify/
|
1
|
+
require "objectify/config/policies"
|
2
2
|
require "objectify/executor"
|
3
3
|
require "objectify/policy_chain_executor"
|
4
4
|
require "objectify/instrumentation"
|
@@ -20,17 +20,17 @@ module Objectify
|
|
20
20
|
objectify.injector
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
klass = Objectify::
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
def request_injectables_context
|
24
|
+
klass = Objectify::Config::Injectables
|
25
|
+
@request_injectables_context ||= klass.new.tap do |injectables_context|
|
26
|
+
injectables_context.add_value(:controller, self)
|
27
|
+
injectables_context.add_value(:params, params)
|
28
|
+
injectables_context.add_value(:session, session)
|
29
|
+
injectables_context.add_value(:cookies, cookies)
|
30
|
+
injectables_context.add_value(:request, request)
|
31
|
+
injectables_context.add_value(:response, response)
|
32
|
+
injectables_context.add_value(:flash, flash)
|
33
|
+
injectables_context.add_value(:renderer, Renderer.new(self))
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -65,14 +65,14 @@ module Objectify
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def objectify_around_filter
|
68
|
-
objectify.
|
68
|
+
objectify.injectables.context = request_injectables_context
|
69
69
|
yield
|
70
|
-
objectify.
|
70
|
+
objectify.injectables.context = nil
|
71
71
|
end
|
72
72
|
|
73
73
|
def execute_objectify_action
|
74
74
|
service_result = objectify_executor.call(action.service, :service)
|
75
|
-
|
75
|
+
request_injectables_context.add_value(:service_result, service_result)
|
76
76
|
|
77
77
|
objectify_executor.call(action.responder, :responder)
|
78
78
|
end
|
@@ -38,8 +38,16 @@ module Objectify
|
|
38
38
|
@application.objectify.append_policy_responders(options)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
@application.objectify.
|
41
|
+
def implementations(options)
|
42
|
+
@application.objectify.append_implementations(options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def resolvers(options)
|
46
|
+
@application.objectify.append_resolvers(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def values(options)
|
50
|
+
@application.objectify.append_values(options)
|
43
51
|
end
|
44
52
|
|
45
53
|
def legacy_action(controller, actions, options)
|
data/lib/objectify/version.rb
CHANGED
data/spec/config/context_spec.rb
CHANGED
@@ -70,19 +70,6 @@ describe "Objectify::Config::Context" do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
context "appending resolutions" do
|
74
|
-
before do
|
75
|
-
@locator = stub("ResolverLocator", :add => nil)
|
76
|
-
@context = Objectify::Config::Context.new(nil)
|
77
|
-
@context.locator = @locator
|
78
|
-
@context.append_resolutions :something => String.new
|
79
|
-
end
|
80
|
-
|
81
|
-
it "adds them to a locator it has" do
|
82
|
-
@locator.should have_received(:add).with(:something, String.new)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
73
|
context "the legacy_action" do
|
87
74
|
before do
|
88
75
|
@action = stub("Action")
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "objectify/config/injectables"
|
3
|
+
|
4
|
+
describe "Objectify::Config::Injectables" do
|
5
|
+
before do
|
6
|
+
@injectables = Objectify::Config::Injectables.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "accepts new resolvers" do
|
10
|
+
@injectables.add_resolver(:a, :b)
|
11
|
+
@injectables.get(:a).should == [:resolver, :b]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "accepts new implementations" do
|
15
|
+
@injectables.add_implementation(:a, :b)
|
16
|
+
@injectables.get(:a).should == [:implementation, :b]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "accepts new values" do
|
20
|
+
@injectables.add_value(:a, :b)
|
21
|
+
@injectables.get(:a).should == [:value, :b]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns unknown if the value is unknown" do
|
25
|
+
@injectables.get(:c).should == [:unknown, :c]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can merge configs" do
|
29
|
+
@injectables = Objectify::Config::Injectables.new :a => [:implementation, :b]
|
30
|
+
@injectables2 = Objectify::Config::Injectables.new :a => [:implementation, :c]
|
31
|
+
@injectables.merge(@injectables2).get(:a).should == [:implementation, :c]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts a context that it falls back to" do
|
35
|
+
@context = Objectify::Config::Injectables.new
|
36
|
+
@context.add_value :controller, :something
|
37
|
+
@injectables.context = @context
|
38
|
+
@injectables.get(:controller).should == [:value, :something]
|
39
|
+
end
|
40
|
+
end
|
data/spec/injector_spec.rb
CHANGED
@@ -18,6 +18,11 @@ describe "Objectify::Injector" do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def optional_arg(asdf=true)
|
21
|
+
"other value"
|
22
|
+
end
|
23
|
+
|
24
|
+
def no_args
|
25
|
+
"value"
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
@@ -34,55 +39,64 @@ describe "Objectify::Injector" do
|
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@resolver_locator = stub("ResolverLocator", :call => @resolver)
|
42
|
-
@injector = Objectify::Injector.new(@resolver_locator)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "can constructor inject based on method name using a simple resolver" do
|
46
|
-
@injector.call(MyInjectedClass, :new).some_dependency.should == @dependency
|
47
|
-
end
|
42
|
+
before do
|
43
|
+
@config = stub("Config", :get => nil)
|
44
|
+
@injector = Objectify::Injector.new(@config)
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
context "when there aren't any parameters" do
|
48
|
+
it "can call the method" do
|
49
|
+
obj = MyInjectedClass.new(nil)
|
50
|
+
@injector.call(obj, :no_args).should == "value"
|
52
51
|
end
|
53
52
|
|
54
|
-
it "
|
55
|
-
|
56
|
-
@
|
53
|
+
it "can call a method with optional parameters" do
|
54
|
+
obj = MyInjectedClass.new(nil)
|
55
|
+
@injector.call(obj, :optional_arg).should == "other value"
|
57
56
|
end
|
57
|
+
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
context "when there is a simple value parameter" do
|
60
|
+
it "injects that value" do
|
61
|
+
@config.stubs(:get).with(:params).returns([:value, 1])
|
62
|
+
obj = MyInjectedClass.new(nil)
|
63
|
+
@injector.call(obj, :requires_params).should == 1
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
:params
|
67
|
+
context "when there is a resolver param" do
|
68
|
+
it "instantiates and calls the resolver" do
|
69
|
+
@config.stubs(:get).with(:params).returns([:resolver, :simple])
|
70
|
+
@config.stubs(:get).with(:something).returns([:value, :SOMETHING])
|
71
|
+
obj = MyInjectedClass.new(nil)
|
72
|
+
@injector.call(obj, :requires_params).should == :SOMETHING
|
68
73
|
end
|
74
|
+
end
|
69
75
|
|
70
|
-
|
71
|
-
|
76
|
+
context "when there is an implementation param" do
|
77
|
+
it "instantiates and calls the resolver" do
|
78
|
+
@config.stubs(:get).with(:params).returns([:implementation, :simple_resolver])
|
79
|
+
@config.stubs(:get).with(:something).returns([:value, :SOMETHING])
|
80
|
+
obj = MyInjectedClass.new(nil)
|
81
|
+
@injector.call(obj, :requires_params).call.should == :SOMETHING
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
75
|
-
context "
|
76
|
-
|
77
|
-
@
|
78
|
-
@
|
79
|
-
|
85
|
+
context "when there is an unconfigured resolver param" do
|
86
|
+
it "instantiates and calls the resolver" do
|
87
|
+
@config.stubs(:get).with(:params).returns([:unknown, :simple])
|
88
|
+
@config.stubs(:get).with(:something).returns([:value, :SOMETHING])
|
89
|
+
obj = MyInjectedClass.new(nil)
|
90
|
+
@injector.call(obj, :requires_params).should == :SOMETHING
|
80
91
|
end
|
92
|
+
end
|
81
93
|
|
82
|
-
|
83
|
-
|
84
|
-
@
|
85
|
-
|
94
|
+
context "when there is an unconfigured impl param" do
|
95
|
+
it "instantiates and calls the resolver" do
|
96
|
+
@config.stubs(:get).with(:params).returns([:unknown, :simple_resolver])
|
97
|
+
@config.stubs(:get).with(:something).returns([:value, :SOMETHING])
|
98
|
+
obj = MyInjectedClass.new(nil)
|
99
|
+
@injector.call(obj, :requires_params).call.should == :SOMETHING
|
86
100
|
end
|
87
101
|
end
|
88
102
|
end
|
data/spec/rails/routing_spec.rb
CHANGED
@@ -8,7 +8,9 @@ describe "Objectify::Rails::Routing::ObjectifyMapper" do
|
|
8
8
|
:append_defaults => nil,
|
9
9
|
:append_action => nil,
|
10
10
|
:policies => @policies,
|
11
|
-
:
|
11
|
+
:append_implementations => nil,
|
12
|
+
:append_resolvers => nil,
|
13
|
+
:append_values => nil,
|
12
14
|
:objectify_controller => "some_controller")
|
13
15
|
@rails_mapper = stub("RailsMapper", :resources => nil,
|
14
16
|
:match => nil)
|
@@ -138,14 +140,25 @@ describe "Objectify::Rails::Routing::ObjectifyMapper" do
|
|
138
140
|
end
|
139
141
|
end
|
140
142
|
|
141
|
-
context "adding
|
142
|
-
|
143
|
+
context "adding injector config" do
|
144
|
+
it "hands resolvers to the context" do
|
143
145
|
@opts = { :authenticated => :unauthenticated_responder }
|
144
|
-
@mapper.
|
146
|
+
@mapper.resolvers @opts
|
147
|
+
@objectify.should have_received(:append_resolvers).
|
148
|
+
with(@opts)
|
145
149
|
end
|
146
150
|
|
147
|
-
it "hands
|
148
|
-
@
|
151
|
+
it "hands implementations to the context" do
|
152
|
+
@opts = { :authenticated => :unauthenticated_responder }
|
153
|
+
@mapper.implementations @opts
|
154
|
+
@objectify.should have_received(:append_implementations).
|
155
|
+
with(@opts)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "hands values to the context" do
|
159
|
+
@opts = { :authenticated => :unauthenticated_responder }
|
160
|
+
@mapper.values @opts
|
161
|
+
@objectify.should have_received(:append_values).
|
149
162
|
with(@opts)
|
150
163
|
end
|
151
164
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Rethinking the injector
|
2
|
+
|
3
|
+
Example types of injectables:
|
4
|
+
|
5
|
+
* :current_user => A class that we need to instantiate and call #call on to get the current_user.
|
6
|
+
* :session_min_age_function => A function value that can be injected as-is.
|
7
|
+
* :user_finder => Sometimes a constant, sometimes the name of a class that needs to be instantiated.
|
8
|
+
* :session_creation_service => The name of a class that gets instantiated before it gets injected.
|
9
|
+
|
10
|
+
## Decorators
|
11
|
+
|
12
|
+
Do we want to make decorators first class? My inclination is to say yes. I'd love to have something that looked like this:
|
13
|
+
|
14
|
+
objectify.decorate "objectify_auth/session_creation_service" => ["objectify_auth/session_creation_service/with_email",
|
15
|
+
"objectify_auth/session_creation_service/with_last_pageview"]
|
16
|
+
|
17
|
+
The above would mean that in the following case:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class ObjectifyAuth::SessionCreationService::WithEmail
|
21
|
+
def initialize(session_creation_service)
|
22
|
+
@session_creation_service = session_creation_service
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(user, session)
|
26
|
+
@session_creation_service.call(user, session)
|
27
|
+
session[:e] = user.email
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ObjectifyAuth::SessionCreationService::WithLastPageview
|
32
|
+
def initialize(session_creation_service, time)
|
33
|
+
@session_creation_service = session_creation_service
|
34
|
+
@time = time
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(user, session)
|
38
|
+
@session_creation_service.call(user, session)
|
39
|
+
session[:l] = @time.now
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
...WithEmail's `session_creation_service` parameter would resolve to the base SessionCreationService, and WithLastPageview's `session_creation_service` parameter would resolve to the WithEmail instance.
|
45
|
+
|
46
|
+
Also, the ability to override decorations at the resource or action level:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
objectify.resources :pictures, :decorate => {:picture_creation_service => :generic_service_instrumentation}
|
50
|
+
```
|
51
|
+
|
52
|
+
Also, injectables in general:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
objectify.resources :pictures, :resolve => {:storage_service => :s3_storage_service}
|
56
|
+
objectify.resources :videos, :resolve => {:storage_service => :riak_storage_service}
|
57
|
+
```
|
58
|
+
|
59
|
+
## Ideas on API
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
objectify.resolvers :current_user => "objectify_auth/current_user" # this would automatically append "Resolver"
|
63
|
+
objectify.injectables :session_min_age_function => lambda { 1.month.ago },
|
64
|
+
:user_finder => User,
|
65
|
+
:session_creation_service => :"objectify_auth/session_creation_service"
|
66
|
+
```
|
67
|
+
|
68
|
+
So basically we'd have resolvers (things that need to be instaniated called to get an injectable from), and injectables which are either class names that need to be instantiated (if they're symbols) or values.
|
69
|
+
|
70
|
+
Do we always need to be explicit? If there's a parameter called session_creation_service, should we automatically try to constantize that if we don't have a way to resolve it? If the answer to that question is yes (which I think it is), then should we allow people to import a namespace?
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
objectify.injectable_namespace "objectify_auth"
|
74
|
+
```
|
75
|
+
|
76
|
+
If not that, then we probably need *something* or any kind of library is going to require a lot of manual configuration to get going.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: objectify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70137374227200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 2.4.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70137374227200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bundler
|
27
|
-
requirement: &
|
27
|
+
requirement: &70137374226220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70137374226220
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: jeweler
|
38
|
-
requirement: &
|
38
|
+
requirement: &70137374224820 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.6.4
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70137374224820
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bourne
|
49
|
-
requirement: &
|
49
|
+
requirement: &70137374222400 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - =
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '1.0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70137374222400
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: mocha
|
60
|
-
requirement: &
|
60
|
+
requirement: &70137374221700 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - =
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.9.8
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70137374221700
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rails
|
71
|
-
requirement: &
|
71
|
+
requirement: &70137378002540 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 3.0.0
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70137378002540
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: i18n
|
82
|
-
requirement: &
|
82
|
+
requirement: &70137378000720 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70137378000720
|
91
91
|
description: Objects on rails.
|
92
92
|
email:
|
93
93
|
- jamesgolick@gmail.com
|
@@ -99,13 +99,13 @@ files:
|
|
99
99
|
- .gitignore
|
100
100
|
- .rspec
|
101
101
|
- Gemfile
|
102
|
-
- Gemfile.lock
|
103
102
|
- LICENSE.txt
|
104
103
|
- README.md
|
105
104
|
- Rakefile
|
106
105
|
- lib/objectify.rb
|
107
106
|
- lib/objectify/config/action.rb
|
108
107
|
- lib/objectify/config/context.rb
|
108
|
+
- lib/objectify/config/injectables.rb
|
109
109
|
- lib/objectify/config/policies.rb
|
110
110
|
- lib/objectify/executor.rb
|
111
111
|
- lib/objectify/injector.rb
|
@@ -127,6 +127,7 @@ files:
|
|
127
127
|
- objectify.gemspec
|
128
128
|
- spec/config/action_spec.rb
|
129
129
|
- spec/config/context_spec.rb
|
130
|
+
- spec/config/injectables_spec.rb
|
130
131
|
- spec/config/policies_spec.rb
|
131
132
|
- spec/executor_spec.rb
|
132
133
|
- spec/injector_spec.rb
|
@@ -138,6 +139,7 @@ files:
|
|
138
139
|
- spec/resolver_spec.rb
|
139
140
|
- spec/route_spec.rb
|
140
141
|
- spec/spec_helper.rb
|
142
|
+
- thoughts/injector.md
|
141
143
|
homepage: https://github.com/bitlove/objectify
|
142
144
|
licenses: []
|
143
145
|
post_install_message:
|
@@ -165,6 +167,7 @@ summary: Objects on rails.
|
|
165
167
|
test_files:
|
166
168
|
- spec/config/action_spec.rb
|
167
169
|
- spec/config/context_spec.rb
|
170
|
+
- spec/config/injectables_spec.rb
|
168
171
|
- spec/config/policies_spec.rb
|
169
172
|
- spec/executor_spec.rb
|
170
173
|
- spec/injector_spec.rb
|