forked from garybernhardt/raptor
/
injector.rb
148 lines (126 loc) · 3.58 KB
/
injector.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
module Raptor
class Injector
def initialize(injectables=[])
@injectables = injectables
end
def self.for_app_module(app_module)
Injector.new([CustomInjectable.new(app_module)])
end
def sources
# Merge all injectables' sources into a single hash
@sources ||= @injectables.map do |injectable|
injectable.sources(self)
end.inject(&:merge)
end
def call(method)
args = self.args(method)
Raptor.log("Injecting #{method.inspect} with #{args.inspect}")
method.call(*args)
end
def args(method)
method = injection_method(method)
parameters(method).select do |type, name|
name && type != :rest && type != :block
end.map do |type, name|
source_proc = sources[name] or raise UnknownInjectable.new(name)
source_proc.call
end
end
def parameters(method)
method.parameters
end
def injection_method(method)
if method.name == :new
method.receiver.instance_method(:initialize)
else
method
end
end
def add_subject(subject)
Injector.new(@injectables +
[Raptor::Injectables::Fixed.new(:subject, subject)])
end
def add_request(request)
injectables = @injectables + [Injectables::Request.new(request)]
Injector.new(injectables)
end
def add_route_path(request, route_path)
injectables = @injectables + [Injectables::RouteVariable.new(request,
route_path)]
Injector.new(injectables)
end
end
class UnknownInjectable < RuntimeError
def initialize(name)
super("Unknown injectable name: #{name.inspect}")
end
end
class CustomInjectable
def initialize(app_module)
@app_module = app_module
end
def sources(injector)
injectables(injector).map do |injectable|
injectable.sources(injector)
end.inject(&:merge) || {}
end
def injectables(injector)
begin
injectables_module = @app_module::Injectables
rescue NameError
return []
end
injectables_module.constants.map do |const_name|
injectables_module.const_get(const_name)
end.select do |const|
const.is_a?(Class)
end.map do |const|
injector.call(const.method(:new))
end
end
end
module Injectables
class Request
def initialize(request)
@request = request
end
def sources(injector)
{:request => lambda { @request },
:http_method => lambda { @request.request_method },
:path => lambda { @request.path_info },
:params => lambda { @request.params },
:env => lambda { @request.env }
}
end
end
class RouteVariable
def initialize(request, route_path)
@request = request
@route_path = route_path
end
def sources(injector)
Hash[path_component_pairs.map do |name, value|
[name, lambda { value }]
end]
end
def path_component_pairs
all_pairs = @route_path.split('/').zip(@request.path_info.split('/'))
variable_pairs = all_pairs.select do |name, value|
name =~ /^:/
end
variable_pairs_without_colons = variable_pairs.map do |name, value|
[name.sub(/^:/, "").to_sym, value]
end
variable_pairs_without_colons
end
end
class Fixed
def initialize(name, value)
@name, @value = name, value
end
def sources(injector)
{@name => lambda { @value } }
end
end
end
end