Skip to content

Commit bc3252a

Browse files
committed
DI: Automatically infer and annotate dependencies for injection
1 parent cc0b870 commit bc3252a

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

src/Angular/DI.purs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
module Angular.DI (
2+
Dependency
3+
, Service
4+
, RootScope(..)
5+
, RootElement(..)
6+
, get
7+
, Injectable
8+
, Annotated()
9+
, annotate
10+
) where
11+
12+
import Control.Monad.Eff (Eff())
13+
import Data.Function
14+
import Angular.Injector (Injector(), InjEff())
15+
import Angular.Animate (Animate())
16+
import Angular.Cache (CacheFactory())
17+
import Angular.Http (Http())
18+
import Angular.Interpolate (Interpolate())
19+
import Angular.Interval (Interval())
20+
import Angular.Location (Location())
21+
import Angular.Log (Log())
22+
import Angular.Parse (Parse())
23+
import Angular.Q (Q())
24+
import Angular.Timeout (Timeout())
25+
import Angular.Scope (Scope())
26+
import Angular.Element (Element())
27+
import Angular.Attributes (Attributes())
28+
import Angular.This (This())
29+
30+
-- | A type which can by provided by dependency injection.
31+
class Dependency a where
32+
name :: String
33+
34+
-- | Services which can be retrieved with get
35+
class (Dependency a) <= Service a
36+
37+
instance dependencyInjector :: Dependency Injector where
38+
name = "$injector"
39+
instance serviceInjector :: Service Injector
40+
41+
instance dependencyAnimate :: Dependency Animate where
42+
name = "$animate"
43+
instance serviceAnimate :: Service Animate
44+
45+
instance dependencyCacheFactory :: Dependency CacheFactory where
46+
name = "$cacheFactory"
47+
instance serviceCacheFactory :: Service CacheFactory
48+
49+
instance dependencyHttp :: Dependency Http where
50+
name = "$http"
51+
instance serviceHttp :: Service Http
52+
53+
instance dependencyInterpolate :: Dependency Interpolate where
54+
name = "$interpolate"
55+
instance serviceInterpolate :: Service Interpolate
56+
57+
instance dependencyInterval :: Dependency Interval where
58+
name = "$interval"
59+
instance serviceInterval :: Service Interval
60+
61+
instance dependencyLocation :: Dependency Location where
62+
name = "$location"
63+
instance serviceLocation :: Service Location
64+
65+
instance dependencyLog :: Dependency Log where
66+
name = "$log"
67+
instance serviceLog :: Service Log
68+
69+
instance dependencyParse :: Dependency Parse where
70+
name = "$parse"
71+
instance serviceParse :: Service Parse
72+
73+
instance dependencyQ :: Dependency Q where
74+
name = "$q"
75+
instance serviceQ :: Service Q
76+
77+
instance dependencyTimeout :: Dependency Timeout where
78+
name = "$timeout"
79+
instance serviceTimeout :: Service Timeout
80+
81+
newtype RootScope a = RootScope (Scope a)
82+
83+
instance dependencyRootScope :: Dependency (RootScope a) where
84+
name = "$rootScope"
85+
instance serviceRootScope :: Service (RootScope a)
86+
87+
instance dependencyScope :: Dependency (Scope a) where
88+
name = "$scope"
89+
90+
newtype RootElement = RootElement Element
91+
92+
instance dependencyRootElement :: Dependency RootElement where
93+
name = "$rootElement"
94+
instance serviceRootElement :: Service RootElement
95+
96+
instance dependencyElement :: Dependency Element where
97+
name = "$element"
98+
99+
instance dependencyAttributes :: Dependency Attributes where
100+
name = "$attrs"
101+
102+
instance dependencyThis :: Dependency (This a) where
103+
-- it would be nice to make a dummy service to avoid the special handling
104+
-- in annotate, but there is nowhere to do so
105+
name = "$this"
106+
107+
108+
foreign import getDependency
109+
"function get(dependency) {\
110+
\ dependency = dependency.name;\
111+
\ return function ($injector) {\
112+
\ return function () {\
113+
\ return $injector.get(dependency);\
114+
\ };\
115+
\ };\
116+
\}" :: forall e a . (Dependency a) => Injector -> InjEff e a
117+
118+
get :: forall e a . (Service a) => Injector -> InjEff e a
119+
get = getDependency
120+
121+
122+
class Injectable a where
123+
dependencies :: a -> [String]
124+
125+
instance injectableEff :: Injectable (Eff e r) where
126+
dependencies _ = []
127+
128+
foreign import dependenciesFn
129+
"function dependenciesFn(dependency) {\
130+
\ dependency = [dependency.name];\
131+
\ return function (injectable) {\
132+
\ return function (/*f*/) {\
133+
\ return dependency.concat(injectable.dependencies(/*f(a)*/));\
134+
\ };\
135+
\ };\
136+
\}" :: forall a b . (Dependency a, Injectable b) => (a -> b) -> [String]
137+
138+
instance injectableFn :: (Dependency a, Injectable b) => Injectable (a -> b) where
139+
dependencies f = dependenciesFn f
140+
141+
142+
-- | An Eff function with added dependency injection annotations.
143+
foreign import data Annotated :: * -> *
144+
145+
-- | Infer and annotate a function with its dependencies, that can provided by Injector.invoke or Module functions.
146+
foreign import annotate
147+
"function annotate(injectable) {\
148+
\ return function (fun) {\
149+
\ var inject = injectable.dependencies(fun);\
150+
\ function g(/*...*/) {\
151+
\ var f = fun;\
152+
\ for (var i = 0, a = 0; i < inject.length; i++)\
153+
\ f = f(inject[i] === '$this' ? this : arguments[a++]);\
154+
\ return f();\
155+
\ }\
156+
\ g.prototype = fun.prototype;\
157+
\ g.$inject = inject.filter(function (d) { return d !== '$this'; });\
158+
\ return g;\
159+
\ };\
160+
\}" :: forall a . (Injectable a) => a -> Annotated a

0 commit comments

Comments
 (0)