Skip to content

Commit d2a00ff

Browse files
committed
Merge pull request #4 from daveray/static-core-clj
Static core clj
2 parents 39239fa + 4fa627d commit d2a00ff

File tree

9 files changed

+344
-133
lines changed

9 files changed

+344
-133
lines changed

language-adaptors/rxjava-clojure/README.md

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,57 @@
11
# Clojure Adaptor for RxJava
22

3+
This adaptor provides functions and macros to ease Clojure/RxJava interop. In particular, there are functions and macros for turning Clojure functions and code into RxJava `Func*` and `Action*` interfaces without the tedium of manually reifying the interfaces.
34

4-
This adaptor allows 'fn' functions to be used and RxJava will know how to invoke them.
5+
# Basic Usage
56

6-
This enables code such as:
7+
## Requiring the interop namespace
8+
The first thing to do is to require the namespace:
79

810
```clojure
9-
(->
10-
(Observable/toObservable ["one" "two" "three"])
11-
(.take 2)
12-
(.subscribe (fn [arg] (println arg))))
11+
(ns my.namespace
12+
(:require [rx.lang.clojure.interop :as rx])
13+
(:import [rx Observable]))
1314
```
1415

15-
This still dependes on Clojure using Java interop against the Java API.
16+
or, at the REPL:
1617

17-
A future goal is a Clojure wrapper to expose the functions in a more idiomatic way.
18+
```clojure
19+
(require '[rx.lang.clojure.interop :as rx])
20+
```
21+
22+
## Using rx/fn
23+
Once the namespace is required, you can use the `rx/fn` macro anywhere RxJava wants a `rx.util.functions.Func` object. The syntax is exactly the same as `clojure.core/fn`:
24+
25+
```clojure
26+
(-> my-observable
27+
(.map (rx/fn [v] (* 2 v))))
28+
```
29+
30+
If you already have a plain old Clojure function you'd like to use, you can pass it to the `rx/fn*` function to get a new object that implements `rx.util.functions.Func`:
31+
32+
```clojure
33+
(-> my-numbers
34+
(.reduce (rx/fn* +)))
35+
```
36+
37+
## Using rx/action
38+
The `rx/action` macro is identical to `rx/fn` except that the object returned implements `rx.util.functions.Action` interfaces. It's used in `subscribe` and other side-effect-y contexts:
39+
40+
```clojure
41+
(-> my-observable
42+
(.map (rx/fn* transform-data))
43+
(.finallyDo (rx/action [] (println "Finished transform")))
44+
(.subscribe (rx/action [v] (println "Got value" v))
45+
(rx/action [e] (println "Get error" e))
46+
(rx/action [] (println "Sequence complete"))))
47+
```
48+
49+
# Gotchas
50+
Here are a few things to keep in mind when using this interop:
1851

52+
* Keep in mind the (mostly empty) distinction between `Func` and `Action` and which is used in which contexts
53+
* If there are multiple Java methods overloaded by `Func` arity, you'll need to use a type hint to let the compiler know which one to choose.
54+
* Methods that take a predicate (like filter) expect the predicate to return a boolean value. A function that returns a non-boolean value will result in a `ClassCastException`.
1955

2056
# Binaries
2157

language-adaptors/rxjava-clojure/build.gradle

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ apply plugin: 'osgi'
44
dependencies {
55
compile project(':rxjava-core')
66

7-
provided 'junit:junit-dep:4.10'
8-
provided 'org.mockito:mockito-core:1.8.5'
9-
107
// clojure
118
compile 'org.clojure:clojure:1.4.+'
12-
compile 'clj-http:clj-http:0.6.4' // https://clojars.org/clj-http
9+
//compile 'clj-http:clj-http:0.6.4' // https://clojars.org/clj-http
1310
}
1411

1512
/*
@@ -20,7 +17,7 @@ warnOnReflection = true
2017

2118
buildscript {
2219
repositories { maven { url "http://clojars.org/repo" } }
23-
dependencies { classpath "clojuresque:clojuresque:1.5.4" }
20+
dependencies { classpath "clojuresque:clojuresque:1.5.8" }
2421
}
2522

2623
repositories {
@@ -52,4 +49,4 @@ jar {
5249
instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*'
5350
instruction 'Fragment-Host', 'com.netflix.rxjava.core'
5451
}
55-
}
52+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
(ns rx.lang.clojure.examples.http-examples
2+
(:require [rx.lang.clojure.interop :as rx]
3+
[clj-http.client :as http])
4+
(:import rx.Observable rx.subscriptions.Subscriptions))
5+
6+
; NOTE on naming conventions. I'm using camelCase names (against clojure convention)
7+
; in this file as I'm purposefully keeping functions and methods across
8+
; different language implementations in-sync for easy comparison.
9+
10+
(defn fetchWikipediaArticleAsynchronously [wikipediaArticleNames]
11+
"Fetch a list of Wikipedia articles asynchronously.
12+
13+
return Observable<String> of HTML"
14+
(Observable/create
15+
(rx/fn [observer]
16+
(let [f (future
17+
(doseq [articleName wikipediaArticleNames]
18+
(-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName)))))
19+
; after sending response to onnext we complete the sequence
20+
(-> observer .onCompleted))]
21+
; a subscription that cancels the future if unsubscribed
22+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
23+
24+
; To see output
25+
(comment
26+
(-> (fetchWikipediaArticleAsynchronously ["Tiger" "Elephant"])
27+
(.subscribe (rx/action [v] (println "--- Article ---\n" (subs (:body v) 0 125) "...")))))
28+
29+
30+
; --------------------------------------------------
31+
; Error Handling
32+
; --------------------------------------------------
33+
34+
(defn fetchWikipediaArticleAsynchronouslyWithErrorHandling [wikipediaArticleNames]
35+
"Fetch a list of Wikipedia articles asynchronously
36+
with proper error handling.
37+
38+
return Observable<String> of HTML"
39+
(Observable/create
40+
(rx/fn [observer]
41+
(let [f (future
42+
(try
43+
(doseq [articleName wikipediaArticleNames]
44+
(-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName)))))
45+
;(catch Exception e (prn "exception")))
46+
(catch Exception e (-> observer (.onError e))))
47+
; after sending response to onNext we complete the sequence
48+
(-> observer .onCompleted))]
49+
; a subscription that cancels the future if unsubscribed
50+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
51+
52+
; To see output
53+
(comment
54+
(-> (fetchWikipediaArticleAsynchronouslyWithErrorHandling ["Tiger" "NonExistentTitle" "Elephant"])
55+
(.subscribe (rx/action [v] (println "--- Article ---\n" (subs (:body v) 0 125) "..."))
56+
(rx/action [e] (println "--- Error ---\n" (.getMessage e))))))
57+
58+
Lines changed: 25 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(ns rx.lang.clojure.examples.rx-examples
2-
(:import rx.Observable rx.subscriptions.Subscriptions)
3-
(:require [clj-http.client :as http]))
2+
(:require [rx.lang.clojure.interop :as rx])
3+
(:import rx.Observable rx.subscriptions.Subscriptions))
44

55
; NOTE on naming conventions. I'm using camelCase names (against clojure convention)
66
; in this file as I'm purposefully keeping functions and methods across
@@ -12,8 +12,8 @@
1212

1313
(defn hello
1414
[& args]
15-
(-> (Observable/toObservable args)
16-
(.subscribe #(println (str "Hello " % "!")))))
15+
(-> (Observable/from args)
16+
(.subscribe (rx/action [v] (println (str "Hello " v "!"))))))
1717

1818
; To see output
1919
(comment
@@ -23,22 +23,13 @@
2323
; Create Observable from Existing Data
2424
; --------------------------------------------------
2525

26-
(defn existingDataFromNumbers []
27-
(Observable/toObservable [1 2 3 4 5 6]))
2826

2927
(defn existingDataFromNumbersUsingFrom []
3028
(Observable/from [1 2 3 4 5 6]))
3129

32-
(defn existingDataFromObjects []
33-
(Observable/toObservable ["a" "b" "c"]))
34-
3530
(defn existingDataFromObjectsUsingFrom []
3631
(Observable/from ["a" "b" "c"]))
3732

38-
(defn existingDataFromList []
39-
(let [list [5, 6, 7, 8]]
40-
(Observable/toObservable list)))
41-
4233
(defn existingDataFromListUsingFrom []
4334
(let [list [5, 6, 7, 8]]
4435
(Observable/from list)))
@@ -56,7 +47,7 @@
5647

5748
returns Observable<String>"
5849
(Observable/create
59-
(fn [observer]
50+
(rx/fn [observer]
6051
(doseq [x (range 50)] (-> observer (.onNext (str "value_" x))))
6152
; after sending all values we complete the sequence
6253
(-> observer .onCompleted)
@@ -66,46 +57,26 @@
6657

6758
; To see output
6859
(comment
69-
(.subscribe (customObservableBlocking) println))
60+
(.subscribe (customObservableBlocking) (rx/action* println)))
7061

7162
(defn customObservableNonBlocking []
7263
"This example shows a custom Observable that does not block
7364
when subscribed to as it spawns a separate thread.
7465

7566
returns Observable<String>"
7667
(Observable/create
77-
(fn [observer]
68+
(rx/fn [observer]
7869
(let [f (future
7970
(doseq [x (range 50)]
8071
(-> observer (.onNext (str "anotherValue_" x))))
8172
; after sending all values we complete the sequence
8273
(-> observer .onCompleted))]
8374
; return a subscription that cancels the future
84-
(Subscriptions/create #(future-cancel f))))))
85-
86-
; To see output
87-
(comment
88-
(.subscribe (customObservableNonBlocking) println))
89-
90-
91-
(defn fetchWikipediaArticleAsynchronously [wikipediaArticleNames]
92-
"Fetch a list of Wikipedia articles asynchronously.
93-
94-
return Observable<String> of HTML"
95-
(Observable/create
96-
(fn [observer]
97-
(let [f (future
98-
(doseq [articleName wikipediaArticleNames]
99-
(-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName)))))
100-
; after sending response to onnext we complete the sequence
101-
(-> observer .onCompleted))]
102-
; a subscription that cancels the future if unsubscribed
103-
(Subscriptions/create #(future-cancel f))))))
75+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
10476

10577
; To see output
10678
(comment
107-
(-> (fetchWikipediaArticleAsynchronously ["Tiger" "Elephant"])
108-
(.subscribe #(println "--- Article ---\n" (subs (:body %) 0 125) "..."))))
79+
(.subscribe (customObservableNonBlocking) (rx/action* println)))
10980

11081

11182
; --------------------------------------------------
@@ -119,8 +90,8 @@
11990
(customObservableNonBlocking)
12091
(.skip 10)
12192
(.take 5)
122-
(.map #(str % "_transformed"))
123-
(.subscribe #(println "onNext =>" %))))
93+
(.map (rx/fn [v] (str v "_transformed")))
94+
(.subscribe (rx/action [v] (println "onNext =>" v)))))
12495

12596
; To see output
12697
(comment
@@ -136,7 +107,7 @@
136107

137108
return Observable<Map>"
138109
(Observable/create
139-
(fn [observer]
110+
(rx/fn [observer]
140111
(let [f (future
141112
(try
142113
; simulate fetching user data via network service call with latency
@@ -147,14 +118,14 @@
147118
(-> observer .onCompleted)
148119
(catch Exception e (-> observer (.onError e))))) ]
149120
; a subscription that cancels the future if unsubscribed
150-
(Subscriptions/create #(future-cancel f))))))
121+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
151122

152123
(defn getVideoBookmark [userId, videoId]
153124
"Asynchronously fetch bookmark for video
154125

155126
return Observable<Integer>"
156127
(Observable/create
157-
(fn [observer]
128+
(rx/fn [observer]
158129
(let [f (future
159130
(try
160131
; simulate fetching user data via network service call with latency
@@ -165,13 +136,13 @@
165136
(-> observer .onCompleted)
166137
(catch Exception e (-> observer (.onError e)))))]
167138
; a subscription that cancels the future if unsubscribed
168-
(Subscriptions/create #(future-cancel f))))))
139+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
169140

170141
(defn getVideoMetadata [videoId, preferredLanguage]
171142
"Asynchronously fetch movie metadata for a given language
172143
return Observable<Map>"
173144
(Observable/create
174-
(fn [observer]
145+
(rx/fn [observer]
175146
(let [f (future
176147
(try
177148
; simulate fetching video data via network service call with latency
@@ -190,7 +161,7 @@
190161
(-> observer .onCompleted)
191162
(catch Exception e (-> observer (.onError e))))) ]
192163
; a subscription that cancels the future if unsubscribed
193-
(Subscriptions/create #(future-cancel f))))))
164+
(Subscriptions/create (rx/action [] (future-cancel f)))))))
194165

195166

196167
(defn getVideoForUser [userId videoId]
@@ -200,24 +171,24 @@
200171
- user data
201172
return Observable<Map>"
202173
(let [user-observable (-> (getUser userId)
203-
(.map (fn [user] {:user-name (:name user)
174+
(.map (rx/fn [user] {:user-name (:name user)
204175
:language (:preferred-language user)})))
205176
bookmark-observable (-> (getVideoBookmark userId videoId)
206-
(.map (fn [bookmark] {:viewed-position (:position bookmark)})))
177+
(.map (rx/fn [bookmark] {:viewed-position (:position bookmark)})))
207178
; getVideoMetadata requires :language from user-observable so nest inside map function
208179
video-metadata-observable (-> user-observable
209180
(.mapMany
210181
; fetch metadata after a response from user-observable is received
211-
(fn [user-map]
182+
(rx/fn [user-map]
212183
(getVideoMetadata videoId (:language user-map)))))]
213184
; now combine 3 async sequences using zip
214185
(-> (Observable/zip bookmark-observable video-metadata-observable user-observable
215-
(fn [bookmark-map metadata-map user-map]
186+
(rx/fn [bookmark-map metadata-map user-map]
216187
{:bookmark-map bookmark-map
217188
:metadata-map metadata-map
218189
:user-map user-map}))
219190
; and transform into a single response object
220-
(.map (fn [data]
191+
(.map (rx/fn [data]
221192
{:video-id videoId
222193
:video-metadata (:metadata-map data)
223194
:user-id userId
@@ -231,37 +202,7 @@
231202
(comment
232203
(-> (getVideoForUser 12345 78965)
233204
(.subscribe
234-
(fn [x] (println "--- Object ---\n" x))
235-
(fn [e] (println "--- Error ---\n" e))
236-
(fn [] (println "--- Completed ---")))))
237-
238-
239-
; --------------------------------------------------
240-
; Error Handling
241-
; --------------------------------------------------
242-
243-
(defn fetchWikipediaArticleAsynchronouslyWithErrorHandling [wikipediaArticleNames]
244-
"Fetch a list of Wikipedia articles asynchronously
245-
with proper error handling.
246-
247-
return Observable<String> of HTML"
248-
(Observable/create
249-
(fn [observer]
250-
(let [f (future
251-
(try
252-
(doseq [articleName wikipediaArticleNames]
253-
(-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName)))))
254-
;(catch Exception e (prn "exception")))
255-
(catch Exception e (-> observer (.onError e))))
256-
; after sending response to onNext we complete the sequence
257-
(-> observer .onCompleted))]
258-
; a subscription that cancels the future if unsubscribed
259-
(Subscriptions/create #(future-cancel f))))))
260-
261-
; To see output
262-
(comment
263-
(-> (fetchWikipediaArticleAsynchronouslyWithErrorHandling ["Tiger" "NonExistentTitle" "Elephant"])
264-
(.subscribe #(println "--- Article ---\n" (subs (:body %) 0 125) "...")
265-
#(println "--- Error ---\n" (.getMessage %)))))
266-
205+
(rx/action [x] (println "--- Object ---\n" x))
206+
(rx/action [e] (println "--- Error ---\n" e))
207+
(rx/action [] (println "--- Completed ---")))))
267208

0 commit comments

Comments
 (0)