├── .gitattributes ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── benchmarks └── subject-put │ ├── .gitignore │ ├── dub.json │ └── source │ └── app.d ├── deps-lock.txt ├── deps.dot ├── deps.png ├── deps.svg ├── deps.txt ├── docs.json ├── docs ├── articles │ ├── Overview.ja.md │ └── Practice.ja.md ├── index.html ├── rx.algorithm.all.all.1.html ├── rx.algorithm.all.all.2.html ├── rx.algorithm.all.all.html ├── rx.algorithm.all.html ├── rx.algorithm.any.any.1.html ├── rx.algorithm.any.any.2.html ├── rx.algorithm.any.any.html ├── rx.algorithm.any.html ├── rx.algorithm.buffer.buffered.html ├── rx.algorithm.buffer.html ├── rx.algorithm.combineLatest.combineLatest.html ├── rx.algorithm.combineLatest.html ├── rx.algorithm.debounce.debounce.1.html ├── rx.algorithm.debounce.debounce.2.html ├── rx.algorithm.debounce.debounce.html ├── rx.algorithm.debounce.html ├── rx.algorithm.filter.FilterObservable.html ├── rx.algorithm.filter.FilterObservable.subscribe.html ├── rx.algorithm.filter.FilterObservable.this.html ├── rx.algorithm.filter.filter.html ├── rx.algorithm.filter.html ├── rx.algorithm.fold.fold.html ├── rx.algorithm.fold.html ├── rx.algorithm.groupby.GroupedObservable.html ├── rx.algorithm.groupby.groupBy.html ├── rx.algorithm.groupby.html ├── rx.algorithm.html ├── rx.algorithm.map.html ├── rx.algorithm.map.map.html ├── rx.algorithm.merge.html ├── rx.algorithm.merge.merge.1.html ├── rx.algorithm.merge.merge.2.html ├── rx.algorithm.merge.merge.html ├── rx.algorithm.scan.html ├── rx.algorithm.scan.scan.html ├── rx.algorithm.tee.html ├── rx.algorithm.tee.tee.html ├── rx.algorithm.uniq.distinctUntilChanged.html ├── rx.algorithm.uniq.html ├── rx.algorithm.uniq.uniq.html ├── rx.disposable.AnonymousDisposable.dispose.html ├── rx.disposable.AnonymousDisposable.html ├── rx.disposable.AnonymousDisposable.this.html ├── rx.disposable.Cancelable.html ├── rx.disposable.Cancelable.isDisposed.html ├── rx.disposable.CancelableObject.html ├── rx.disposable.CancelableObject.isDisposed.html ├── rx.disposable.CancelableObject.this.html ├── rx.disposable.CancellationToken.cancel.html ├── rx.disposable.CancellationToken.dispose.html ├── rx.disposable.CancellationToken.html ├── rx.disposable.CancellationToken.isCanceled.html ├── rx.disposable.CancellationToken.isDisposed.html ├── rx.disposable.CompositeDisposable.dispose.html ├── rx.disposable.CompositeDisposable.html ├── rx.disposable.CompositeDisposable.this.html ├── rx.disposable.Disposable.dispose.html ├── rx.disposable.Disposable.html ├── rx.disposable.DisposableObject.dispose.html ├── rx.disposable.DisposableObject.html ├── rx.disposable.DisposableObject.this.html ├── rx.disposable.NopDisposable.html ├── rx.disposable.NopDisposable.instance.html ├── rx.disposable.RefCountDisposable.dispose.html ├── rx.disposable.RefCountDisposable.getDisposable.html ├── rx.disposable.RefCountDisposable.html ├── rx.disposable.RefCountDisposable.this.html ├── rx.disposable.SerialDisposable.disposable.1.html ├── rx.disposable.SerialDisposable.disposable.2.html ├── rx.disposable.SerialDisposable.disposable.html ├── rx.disposable.SerialDisposable.dispose.html ├── rx.disposable.SerialDisposable.html ├── rx.disposable.SerialDisposable.isDisposed.html ├── rx.disposable.SignalDisposable.dispose.html ├── rx.disposable.SignalDisposable.html ├── rx.disposable.SignalDisposable.signal.html ├── rx.disposable.SingleAssignmentDisposable.dispose.html ├── rx.disposable.SingleAssignmentDisposable.html ├── rx.disposable.SingleAssignmentDisposable.isDisposed.html ├── rx.disposable.SingleAssignmentDisposable.setDisposable.html ├── rx.disposable.html ├── rx.disposable.isCancelable.html ├── rx.disposable.isDisposable.html ├── rx.disposable.withDisposed.1.html ├── rx.disposable.withDisposed.2.html ├── rx.disposable.withDisposed.html ├── rx.html ├── rx.observable.Observable.html ├── rx.observable.ObservableObject.html ├── rx.observable.asObservable.html ├── rx.observable.defer.html ├── rx.observable.doSubscribe.1.html ├── rx.observable.doSubscribe.2.html ├── rx.observable.doSubscribe.3.html ├── rx.observable.doSubscribe.4.html ├── rx.observable.doSubscribe.5.html ├── rx.observable.doSubscribe.html ├── rx.observable.empty.html ├── rx.observable.error.html ├── rx.observable.from.html ├── rx.observable.html ├── rx.observable.isObservable.1.html ├── rx.observable.isObservable.2.html ├── rx.observable.isObservable.html ├── rx.observable.isSubscribable.html ├── rx.observable.never.html ├── rx.observer.CompositeObserver.add.html ├── rx.observer.CompositeObserver.completed.html ├── rx.observer.CompositeObserver.empty.html ├── rx.observer.CompositeObserver.failure.html ├── rx.observer.CompositeObserver.html ├── rx.observer.CompositeObserver.observers.html ├── rx.observer.CompositeObserver.put.html ├── rx.observer.CompositeObserver.remove.html ├── rx.observer.CompositeObserver.removeStrict.html ├── rx.observer.CompositeObserver.this.html ├── rx.observer.DoneObserver.html ├── rx.observer.NopObserver.html ├── rx.observer.NopObserver.instance.html ├── rx.observer.Observer.completed.html ├── rx.observer.Observer.failure.html ├── rx.observer.Observer.html ├── rx.observer.ObserverObject.completed.html ├── rx.observer.ObserverObject.failure.html ├── rx.observer.ObserverObject.html ├── rx.observer.hasCompleted.html ├── rx.observer.hasFailure.html ├── rx.observer.html ├── rx.observer.isObserver.html ├── rx.observer.makeObserver.1.html ├── rx.observer.makeObserver.2.html ├── rx.observer.makeObserver.3.html ├── rx.observer.makeObserver.html ├── rx.range.drop.drop.html ├── rx.range.drop.html ├── rx.range.html ├── rx.range.take.html ├── rx.range.take.take.html ├── rx.range.takeLast.html ├── rx.range.takeLast.takeLast.html ├── rx.range.takeUntil.html ├── rx.range.takeUntil.takeUntil.html ├── rx.range.zip.GetElementsTuple.html ├── rx.range.zip.GetZipStoreType.html ├── rx.range.zip.GetZipStoreTypeImpl.GetZipStoreTypeImpl.html ├── rx.range.zip.GetZipStoreTypeImpl.html ├── rx.range.zip.ZipNObservable.ElementType.html ├── rx.range.zip.ZipNObservable.html ├── rx.range.zip.ZipNObservable.subscribe.html ├── rx.range.zip.ZipNObservable.this.html ├── rx.range.zip.ZipStore.html ├── rx.range.zip.html ├── rx.range.zip.zip.html ├── rx.scheduler.AsyncScheduler.html ├── rx.scheduler.AsyncScheduler.schedule.html ├── rx.scheduler.HistoricalScheduler.html ├── rx.scheduler.HistoricalScheduler.schedule.html ├── rx.scheduler.HistoricalScheduler.start.html ├── rx.scheduler.HistoricalScheduler.this.html ├── rx.scheduler.LocalScheduler.html ├── rx.scheduler.LocalScheduler.start.html ├── rx.scheduler.MostDerivedScheduler.MostDerivedScheduler.1.html ├── rx.scheduler.MostDerivedScheduler.MostDerivedScheduler.2.html ├── rx.scheduler.MostDerivedScheduler.MostDerivedScheduler.html ├── rx.scheduler.MostDerivedScheduler.html ├── rx.scheduler.ObserveOnObservable.html ├── rx.scheduler.ObserveOnObservable.subscribe.html ├── rx.scheduler.ObserveOnObservable.this.html ├── rx.scheduler.ObserveOnObserver.completed.html ├── rx.scheduler.ObserveOnObserver.failure.html ├── rx.scheduler.ObserveOnObserver.html ├── rx.scheduler.ObserveOnObserver.put.html ├── rx.scheduler.ObserveOnObserver.this.1.html ├── rx.scheduler.ObserveOnObserver.this.2.html ├── rx.scheduler.ObserveOnObserver.this.html ├── rx.scheduler.Scheduler.html ├── rx.scheduler.Scheduler.start.html ├── rx.scheduler.SchedulerObject.html ├── rx.scheduler.SchedulerObject.schedule.html ├── rx.scheduler.SchedulerObject.start.html ├── rx.scheduler.SchedulerObject.this.1.html ├── rx.scheduler.SchedulerObject.this.2.html ├── rx.scheduler.SchedulerObject.this.html ├── rx.scheduler.SubscribeOnObservable.html ├── rx.scheduler.SubscribeOnObservable.subscribe.html ├── rx.scheduler.SubscribeOnObservable.this.html ├── rx.scheduler.TaskPoolScheduler.html ├── rx.scheduler.TaskPoolScheduler.schedule.html ├── rx.scheduler.TaskPoolScheduler.start.html ├── rx.scheduler.TaskPoolScheduler.this.html ├── rx.scheduler.ThreadScheduler.html ├── rx.scheduler.ThreadScheduler.schedule.html ├── rx.scheduler.ThreadScheduler.start.html ├── rx.scheduler.currentScheduler.1.html ├── rx.scheduler.currentScheduler.2.html ├── rx.scheduler.currentScheduler.html ├── rx.scheduler.html ├── rx.scheduler.isAsyncScheduler.html ├── rx.scheduler.isScheduler.html ├── rx.scheduler.observeOn.html ├── rx.scheduler.subscribeOn.html ├── rx.subject.AsyncSubject.completed.html ├── rx.subject.AsyncSubject.failure.html ├── rx.subject.AsyncSubject.html ├── rx.subject.AsyncSubject.put.html ├── rx.subject.AsyncSubject.subscribe.1.html ├── rx.subject.AsyncSubject.subscribe.2.html ├── rx.subject.AsyncSubject.subscribe.html ├── rx.subject.AsyncSubject.unsubscribe.html ├── rx.subject.BehaviorSubject.completed.html ├── rx.subject.BehaviorSubject.failure.html ├── rx.subject.BehaviorSubject.html ├── rx.subject.BehaviorSubject.put.html ├── rx.subject.BehaviorSubject.subscribe.1.html ├── rx.subject.BehaviorSubject.subscribe.2.html ├── rx.subject.BehaviorSubject.subscribe.html ├── rx.subject.BehaviorSubject.this.1.html ├── rx.subject.BehaviorSubject.this.2.html ├── rx.subject.BehaviorSubject.this.html ├── rx.subject.BehaviorSubject.value.1.html ├── rx.subject.BehaviorSubject.value.2.html ├── rx.subject.BehaviorSubject.value.html ├── rx.subject.ReplaySubject.completed.html ├── rx.subject.ReplaySubject.failure.html ├── rx.subject.ReplaySubject.html ├── rx.subject.ReplaySubject.put.html ├── rx.subject.ReplaySubject.subscribe.1.html ├── rx.subject.ReplaySubject.subscribe.2.html ├── rx.subject.ReplaySubject.subscribe.html ├── rx.subject.ReplaySubject.this.html ├── rx.subject.Subject.html ├── rx.subject.SubjectObject.completed.html ├── rx.subject.SubjectObject.failure.html ├── rx.subject.SubjectObject.html ├── rx.subject.SubjectObject.put.html ├── rx.subject.SubjectObject.subscribe.1.html ├── rx.subject.SubjectObject.subscribe.2.html ├── rx.subject.SubjectObject.subscribe.html ├── rx.subject.SubjectObject.this.html ├── rx.subject.SubjectObject.unsubscribe.html ├── rx.subject.asBehaviorSubject.html ├── rx.subject.asReplaySubject.html ├── rx.subject.html ├── rx.util.AtomicCounter.html ├── rx.util.AtomicCounter.isZero.html ├── rx.util.AtomicCounter.this.html ├── rx.util.AtomicCounter.tryDecrement.html ├── rx.util.AtomicCounter.trySetZero.html ├── rx.util.AtomicCounter.tryUpdateCount.html ├── rx.util.EventSignal.html ├── rx.util.EventSignal.setSignal.html ├── rx.util.EventSignal.signal.html ├── rx.util.EventSignal.this.html ├── rx.util.EventSignal.wait.html ├── rx.util.Ticket.html ├── rx.util.TicketBase.html ├── rx.util.TicketBase.isStamped.html ├── rx.util.TicketBase.stamp.html ├── rx.util.assumeThreadLocal.1.html ├── rx.util.assumeThreadLocal.2.html ├── rx.util.assumeThreadLocal.html ├── rx.util.exchange.html ├── rx.util.html ├── script.js ├── search-docs.html ├── search-docs.js ├── search-results.html └── style.css ├── dub.json ├── dub.selections.json ├── examples ├── concurrency │ ├── .gitignore │ ├── dub.json │ └── source │ │ └── app.d ├── fizzbuzz │ ├── .gitignore │ ├── dub.json │ └── source │ │ └── app.d ├── http-client │ ├── curl-ca-bundle.crt │ ├── dub.json │ └── source │ │ └── app.d ├── mvvm-dlangui │ ├── dub.json │ └── source │ │ ├── app.d │ │ └── mvvm │ │ ├── common.d │ │ ├── models │ │ └── simple.d │ │ └── views │ │ └── simple.d ├── mvvm-gtk-d │ ├── .gitignore │ ├── dub.json │ └── source │ │ ├── app.d │ │ └── mvvm │ │ ├── model.d │ │ ├── util.d │ │ └── view.d ├── overview │ ├── dub.json │ └── source │ │ └── app.d ├── rx-dlangui │ ├── dub.json │ └── source │ │ ├── app.d │ │ ├── event.d │ │ ├── model.d │ │ └── win_app.def ├── rx-dnd-gtkd │ ├── dub.sdl │ └── source │ │ └── app.d ├── rx-fswatch │ ├── data │ │ └── data.txt │ ├── dub.json │ ├── dub.selections.json │ └── source │ │ └── app.d ├── rx-gtk-d │ ├── dub.json │ └── source │ │ └── app.d └── scheduler-dlangui │ ├── dub.json │ └── source │ ├── app.d │ ├── utils.d │ └── win_app.def └── source └── rx ├── algorithm ├── all.d ├── any.d ├── buffer.d ├── combineLatest.d ├── debounce.d ├── filter.d ├── fold.d ├── groupby.d ├── map.d ├── merge.d ├── package.d ├── scan.d ├── tee.d └── uniq.d ├── disposable.d ├── observable.d ├── observer.d ├── package.d ├── range ├── drop.d ├── package.d ├── take.d ├── takeLast.d ├── takeUntil.d └── zip.d ├── scheduler.d ├── subject.d └── util.d /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # ========================= 46 | # Dlang / Dub 47 | # ========================= 48 | .dub 49 | *.obj 50 | *.lib 51 | *.exe 52 | *.pdb 53 | *.lst 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: d 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | d: 8 | # support 3 versions 9 | - dmd 10 | - dmd-2.100.2 11 | - dmd-2.099.1 12 | - dmd-2.098.1 13 | - ldc 14 | - ldc-1.30.0 15 | - ldc-1.29.0 16 | - ldc-1.28.0 17 | 18 | script: 19 | - dub test -b unittest-cov 20 | 21 | after_success: 22 | - bash <(curl -s https://codecov.io/bash) 23 | 24 | test: 25 | dub test -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Thank you for your interest! 5 | Issue and pull request are welcome! 6 | 7 | 8 | Development and Testing 9 | ----------------------- 10 | We are weolcoming tha refactoring and implements operators. 11 | 12 | `rx` provides attractive code to users, like the general `std.range` and `std.algorithm`. But, its source code is unfortunately complicated. 13 | 14 | In particular, it is important a variety operators combination tests, and multithreading tests. 15 | 16 | ### Things you will need 17 | 18 | * Linux, Mac OS X, or Windows 19 | * git (used for source version control). 20 | * Some D Compiler. dmd and ldc. 21 | * Latest dub (used for build and testing). 22 | * An IDE. We recommend [Visual Studio Code](https://code.visualstudio.com/download) with [code-d](https://marketplace.visualstudio.com/items?itemName=webfreak.code-d). 23 | * Use dfmt (with default settings) for code formatting. 24 | 25 | ### Patterns 26 | Taking `map` as an example, the operator consists of the following three elements. 27 | 28 | - struct `MapObserver` 29 | - struct `MapObservable` 30 | - template function `map` 31 | 32 | #### MapObserver 33 | Observer's role is to provide concrete algorithms in the constructed pipeline. 34 | 35 | `MapObserver` has the function of passing processed values to Observer as source. 36 | 37 | #### MapObservable 38 | Observable as an operator has Observable as a source and has the role of processing a given Observer and constructing a pipeline. 39 | 40 | `MapObservable` has the function of wrapping the passed Observer in `MapObserver` and subscribing to Observable as source. 41 | 42 | Also, since Observable returns `Disposable`, it relays it. 43 | 44 | #### map 45 | It is utility function for build `MapObservable`. 46 | 47 | 48 | ### Naming priority 49 | Unified naming conventions is important. It is now as follows. 50 | 51 | 1. Look for similar operators from `std.algorithm`. 52 | 2. If there are no similar operators, follow [Reactive X](http://reactivex.io/documentation/operators.html). 53 | 54 | 55 | Documentation 56 | ------------- 57 | We are welcoming the tutorial documentation. 58 | 59 | For users, the goal is to think about the composition of the tutorial so that you can see how to use it, and to add practical examples. 60 | 61 | For developers, We think that the following things are necessary. 62 | 63 | - Manage the tutorial as part of the source. 64 | - Since the contribution log remains, it is easy to understand later. 65 | - Not separate it into another repository to avoid version inconsistency. 66 | - Keep docs as a simple API reference. 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /benchmarks/subject-put/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /subscribers 6 | subscribers.so 7 | subscribers.dylib 8 | subscribers.dll 9 | subscribers.a 10 | subscribers.lib 11 | subscribers-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /benchmarks/subject-put/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "description": "Benchmark for many observers.", 6 | "license": "proprietary", 7 | "name": "subject-put", 8 | "dependencies": { 9 | "rx": { "path": "../../" } 10 | } 11 | } -------------------------------------------------------------------------------- /benchmarks/subject-put/source/app.d: -------------------------------------------------------------------------------- 1 | /+ 2 | Benchmark for many observers. 3 | 4 | [test_rx] 5 | SubjectObject holds the subscribed Observer as an array. 6 | It requires processing time proportional to the number of Observers for one put. 7 | 8 | [test_rx_dispatch] 9 | When it is intended to classify by message type, it is efficient to manage SubjectObject by type using associative arrays. 10 | This idea is "divide and conquer". 11 | +/ 12 | import std.stdio; 13 | import std.algorithm : fold; 14 | import std.conv; 15 | import std.meta; 16 | 17 | import rx; 18 | 19 | import core.sys.windows.windows; 20 | 21 | alias message_t = typeof(MSG.init.message); 22 | enum N = 500; 23 | enum M = 100; 24 | 25 | void main() 26 | { 27 | import std.datetime; 28 | 29 | auto data = makeTestData(); 30 | 31 | auto t1 = Clock.currTime; 32 | auto r1 = test_rx(data); 33 | auto t2 = Clock.currTime; 34 | auto r2 = test_rx_dispatch(data); 35 | auto t3 = Clock.currTime; 36 | 37 | writeln("N : ", N); 38 | writeln("M : ", M); 39 | writeln("test_rx : ", (t2 - t1).total!"msecs"); 40 | writeln("test_rx_dispatch : ", (t3 - t2).total!"msecs"); 41 | 42 | writeln("task1 : ", r1.values.fold!"a+b"(0UL) == N * M ? "success" : "failure"); 43 | writeln("task2 : ", r2.values.fold!"a+b"(0UL) == N * M ? "success" : "failure"); 44 | } 45 | 46 | MSG[] makeTestData() 47 | { 48 | import std.array : appender; 49 | import std.random : uniform; 50 | 51 | auto buf = appender!(MSG[]); 52 | MSG msg; 53 | foreach (_; 0 .. N * M) 54 | { 55 | msg.message = uniform(cast(message_t) 0, cast(message_t) N); 56 | buf.put(msg); 57 | } 58 | return buf.data; 59 | } 60 | 61 | size_t[message_t] test_rx(MSG[] messages) 62 | { 63 | size_t[message_t] counts; 64 | 65 | auto source = new SubjectObject!MSG; 66 | 67 | static foreach (i; 0 .. N) 68 | { 69 | source.filter!(a => a.message == i).doSubscribe!((MSG msg) { counts[msg.message]++; }); 70 | } 71 | 72 | foreach (ref msg; messages) 73 | { 74 | source.put(msg); 75 | } 76 | 77 | return counts; 78 | } 79 | 80 | size_t[message_t] test_rx_dispatch(MSG[] messages) 81 | { 82 | size_t[message_t] counts; 83 | SubjectObject!(MSG)[message_t] sources; 84 | 85 | foreach (message_t i; 0 .. N) 86 | { 87 | sources[i] = new SubjectObject!MSG; 88 | sources[i].doSubscribe!((msg) { counts[msg.message]++; }); 89 | } 90 | 91 | auto source = new SubjectObject!MSG; 92 | source.doSubscribe!((msg) { sources[msg.message].put(msg); }); 93 | 94 | foreach (ref msg; messages) 95 | { 96 | source.put(msg); 97 | } 98 | 99 | return counts; 100 | } 101 | -------------------------------------------------------------------------------- /deps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lempiji/rx/d152c7290737c6f07bf0f7b3c24999a1bc0ce751/deps.png -------------------------------------------------------------------------------- /docs/articles/Practice.ja.md: -------------------------------------------------------------------------------- 1 | # 実践編 2 | 3 | > Note: 文書中の図を正しく表示するためには**PlantUML**が必要です。 4 | 5 | ## GUIアプリケーションへの組み込み 6 | ### 実行スレッドとSchedulerの関係 7 | GUIアプリケーションを作るためのツールキットの多くは、「UIに対する操作」をいわゆる「UIスレッド」で実行しなければならない、という制約があります。 8 | 9 | これはイベントループの操作が公開されたフレームワークでよく見られる制約ですが、たとえば`WinForms`や`WPF`、D言語向けのライブラリでは`dlang-ui`などが該当する非常に一般的な制約です。 10 | 11 | これを解決するためには、「処理がどのスレッドで実行されるか」という点を注意深く確認しながらコーディングする必要があり、非常に手間がかかります。 12 | 13 | 一方、rxにはこれを解決するための`Scheduler`というコンセプトがあります。 14 | 15 | この`Scheduler`を正しく使えば、`subscribe`や`put`/`completed`/`failure`が実行されるスレッドを容易に切り替えることができます。 16 | 17 | 18 | ### Schedulerの目的 19 | GUIアプリケーションにおける`Scheduler`の目的は、多くの場合「重い処理をUIスレッド外へ逃がす」「UIスレッドへ処理をディスパッチする」の2つです。 20 | 21 | 前者は、`subscribeOn`や`observeOn`といったオペレーターに対し、`TaskPoolScheduler`や`ThreadScheduler`を指定することで別スレッドで実行させることができます。これらは組み込みの機能だけで実現できるため、覚えてしまえば特に難しい点はありません。 22 | 23 | 後者は、ツールキット毎に専用の`Scheduler`を作ることで対応する必要があります。このためには、「ツールキットのイベントループを操作する方法」と「`Scheduler`が満たすべき要件」の両方を理解する必要があります。 24 | 25 | > Note: 後者の役割を持った`Scheduler`は、多くの場合`Dispatcher`と呼ばれます。 26 | 27 | ### Dispatcherの作成例(dlang-ui) 28 | Example(scheduler-dlangui)にて、`dlang-ui`向けの`Dispatcher`クラスを公開しています。 29 | 30 | このクラスは、`dlang-ui`が公開している`Timer`クラスを使い、`put`で実行する処理をイベントループにディスパッチします。 31 | これによって、`subscribe`した処理が常にUIスレッドで実行されるようになります。 32 | 33 | ### コード例 34 | 重い処理を「UIスレッドで行う場合」と「ワーカースレッドへ逃がす場合」について、それぞれ疑似コードを示します。 35 | 36 | 適切な`Dispatcher`クラスが定義されているとして、ブロッキングバージョンのロジックに`observeOn`をいくつか挟むだけで実行スレッドを切り替えることができることがわかります。 37 | 38 | ```d 39 | auto sub = SubjectObject!int; 40 | 41 | int heavyFunc(int n) 42 | { 43 | // 重い処理 44 | return result; 45 | } 46 | 47 | auto blocking = sub 48 | .map!(&heavyFunc) 49 | .doSubscribe!(n => label.text = to!string(n)); 50 | 51 | auto nonBlocking = sub 52 | .observeOn(new ThreadScheduler) 53 | .map!(&heavyFunc) 54 | .observeOn(new MyDispatcher) 55 | .doSubscribe!(n => label.text = to!string(n)); 56 | ``` 57 | 58 | これをシーケンス図で比較すると以下のようなイメージになります。 59 | 60 | ```plantuml 61 | @startuml 62 | title blocking 63 | 64 | participant UI 65 | 66 | -> UI : Event 67 | activate UI 68 | 69 | UI -> UI : heavyFunc 70 | 71 | 72 | <- UI 73 | deactivate UI 74 | 75 | @enduml 76 | ``` 77 | 78 | ```plantuml 79 | @startuml 80 | title non-blocking 81 | 82 | participant UI 83 | participant WorkerThread 84 | 85 | -> UI : Event 86 | activate UI 87 | UI ->> WorkerThread : observeOn 88 | activate WorkerThread 89 | 90 | <- UI 91 | deactivate UI 92 | 93 | WorkerThread -> WorkerThread : heavyFunc 94 | 95 | UI <<- WorkerThread : observeOn 96 | deactivate WorkerThread 97 | activate UI 98 | 99 | <- UI 100 | deactivate UI 101 | 102 | @enduml 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | index (index) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

index

Modules

rx
module rx
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.algorithm.all.all.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.algorithm.any.any.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.algorithm.debounce.debounce.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.algorithm.groupby.GroupedObservable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | GroupedObservable (rx.algorithm.groupby.GroupedObservable) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

GroupedObservable

interface GroupedObservable : Observable!E(
TKey
E
) {}
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.algorithm.merge.merge.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.disposable.AnonymousDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AnonymousDisposable.dispose (rx.disposable.AnonymousDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AnonymousDisposable.dispose

class AnonymousDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.AnonymousDisposable.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AnonymousDisposable.this (rx.disposable.AnonymousDisposable.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AnonymousDisposable.this

class AnonymousDisposable
this
(
void delegate(
)
dispose
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.Cancelable.isDisposed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Cancelable.isDisposed (rx.disposable.Cancelable.isDisposed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Cancelable.isDisposed

interface Cancelable
bool
isDisposed
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancelableObject.isDisposed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancelableObject.isDisposed (rx.disposable.CancelableObject.isDisposed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancelableObject.isDisposed

class CancelableObject(T)
bool
isDisposed
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancelableObject.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancelableObject.this (rx.disposable.CancelableObject.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancelableObject.this

class CancelableObject(T)
this
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancellationToken.cancel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancellationToken.cancel (rx.disposable.CancellationToken.cancel) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancellationToken.cancel

class CancellationToken
alias cancel = dispose
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancellationToken.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancellationToken.dispose (rx.disposable.CancellationToken.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancellationToken.dispose

class CancellationToken
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancellationToken.isCanceled.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancellationToken.isCanceled (rx.disposable.CancellationToken.isCanceled) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancellationToken.isCanceled

class CancellationToken
alias isCanceled = isDisposed
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CancellationToken.isDisposed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CancellationToken.isDisposed (rx.disposable.CancellationToken.isDisposed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CancellationToken.isDisposed

class CancellationToken
bool
isDisposed
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.CompositeDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | CompositeDisposable.dispose (rx.disposable.CompositeDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

CompositeDisposable.dispose

class CompositeDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.Disposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Disposable.dispose (rx.disposable.Disposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Disposable.dispose

interface Disposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.DisposableObject.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | DisposableObject.dispose (rx.disposable.DisposableObject.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

DisposableObject.dispose

class DisposableObject(T)
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.DisposableObject.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | DisposableObject.this (rx.disposable.DisposableObject.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

DisposableObject.this

class DisposableObject(T)
this
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.NopDisposable.instance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | NopDisposable.instance (rx.disposable.NopDisposable.instance) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

NopDisposable.instance

class NopDisposable
static
instance
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.RefCountDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | RefCountDisposable.dispose (rx.disposable.RefCountDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

RefCountDisposable.dispose

class RefCountDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.RefCountDisposable.getDisposable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | RefCountDisposable.getDisposable (rx.disposable.RefCountDisposable.getDisposable) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

RefCountDisposable.getDisposable

class RefCountDisposable
getDisposable
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SerialDisposable.disposable.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.disposable.SerialDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SerialDisposable.dispose (rx.disposable.SerialDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SerialDisposable.dispose

class SerialDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SerialDisposable.isDisposed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SerialDisposable.isDisposed (rx.disposable.SerialDisposable.isDisposed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SerialDisposable.isDisposed

class SerialDisposable
bool
isDisposed
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SignalDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SignalDisposable.dispose (rx.disposable.SignalDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SignalDisposable.dispose

class SignalDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SignalDisposable.signal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SignalDisposable.signal (rx.disposable.SignalDisposable.signal) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SignalDisposable.signal

class SignalDisposable
EventSignal
signal
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SingleAssignmentDisposable.dispose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SingleAssignmentDisposable.dispose (rx.disposable.SingleAssignmentDisposable.dispose) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SingleAssignmentDisposable.dispose

class SingleAssignmentDisposable
void
dispose
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.SingleAssignmentDisposable.isDisposed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SingleAssignmentDisposable.isDisposed (rx.disposable.SingleAssignmentDisposable.isDisposed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SingleAssignmentDisposable.isDisposed

class SingleAssignmentDisposable
bool
isDisposed
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.disposable.withDisposed.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.observable.doSubscribe.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.observable.isObservable.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.observer.NopObserver.instance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | NopObserver.instance (rx.observer.NopObserver.instance) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

NopObserver.instance

class NopObserver(E)
static
instance
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.observer.Observer.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Observer.completed (rx.observer.Observer.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Observer.completed

interface Observer(E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.observer.Observer.failure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Observer.failure (rx.observer.Observer.failure) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Observer.failure

interface Observer(E)
void
failure
(
Exception e
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.observer.ObserverObject.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ObserverObject.completed (rx.observer.ObserverObject.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ObserverObject.completed

class ObserverObject(R, E...)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.observer.ObserverObject.failure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ObserverObject.failure (rx.observer.ObserverObject.failure) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ObserverObject.failure

class ObserverObject(R, E...)
void
failure
(
Exception e
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.observer.makeObserver.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.range.drop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | rx.range.drop (rx.range.drop) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

rx.range.drop

This module is a submodule of rx.range. 40 | It provides basic operation a 'drop'

Members

Functions

drop
auto drop(auto ref TObservable observable, size_t n)

Creates the observable that results from discarding the first n elements from the given source.

41 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/rx.range.take.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | rx.range.take (rx.range.take) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

rx.range.take

This module is a submodule of rx.range. 40 | It provides basic operation a 'take'

Members

Functions

take
auto take(auto ref TObservable observable, size_t n)

Creates a sub-observable consisting of only up to the first n elements of the given source.

41 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/rx.range.takeLast.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | rx.range.takeLast (rx.range.takeLast) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

rx.range.takeLast

This module is a submodule of rx.range. 40 | It provides basic operation a 'takeLast'

Members

Functions

takeLast
auto takeLast(auto ref TObservable observable)

Creates a observable that take only a last element of the given source.

41 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/rx.range.takeUntil.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | rx.range.takeUntil (rx.range.takeUntil) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

rx.range.takeUntil

This module is a submodule of rx.range. 40 | It provides basic operation a 'takeUntil'

Members

Functions

takeUntil
auto takeUntil(auto ref TObservable1 source, auto ref TObservable2 stopper)
41 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/rx.range.zip.GetElementsTuple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | GetElementsTuple (rx.range.zip.GetElementsTuple) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

GetElementsTuple

template GetElementsTuple (
Ts...
) {}
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.range.zip.GetZipStoreTypeImpl.GetZipStoreTypeImpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | GetZipStoreTypeImpl.GetZipStoreTypeImpl (rx.range.zip.GetZipStoreTypeImpl.GetZipStoreTypeImpl) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

GetZipStoreTypeImpl.GetZipStoreTypeImpl

template GetZipStoreTypeImpl(TObserver)
alias GetZipStoreTypeImpl(E) = ZipStore!(E, hasCompleted!TObserver)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.range.zip.ZipNObservable.ElementType.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ZipNObservable.ElementType (rx.range.zip.ZipNObservable.ElementType) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ZipNObservable.ElementType

class ZipNObservable(alias selector, TObservables...)
alias ElementType = typeof( ())
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.HistoricalScheduler.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | HistoricalScheduler.this (rx.scheduler.HistoricalScheduler.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

HistoricalScheduler.this

class HistoricalScheduler(T)
this
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.LocalScheduler.start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | LocalScheduler.start (rx.scheduler.LocalScheduler.start) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

LocalScheduler.start

class LocalScheduler
void
start
(
void delegate(
)
op
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.MostDerivedScheduler.MostDerivedScheduler.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.scheduler.ObserveOnObserver.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ObserveOnObserver.completed (rx.scheduler.ObserveOnObserver.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ObserveOnObserver.completed

struct ObserveOnObserver(TObserver, TScheduler, E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.ObserveOnObserver.this.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.scheduler.Scheduler.start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Scheduler.start (rx.scheduler.Scheduler.start) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Scheduler.start

interface Scheduler
void
start
(
void delegate(
)
op
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.SchedulerObject.this.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.scheduler.TaskPoolScheduler.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | TaskPoolScheduler.this (rx.scheduler.TaskPoolScheduler.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

TaskPoolScheduler.this

class TaskPoolScheduler
this
(
TaskPool pool = null
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.ThreadScheduler.start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ThreadScheduler.start (rx.scheduler.ThreadScheduler.start) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ThreadScheduler.start

class ThreadScheduler
void
start
(
void delegate(
)
op
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.scheduler.currentScheduler.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.AsyncSubject.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AsyncSubject.completed (rx.subject.AsyncSubject.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AsyncSubject.completed

class AsyncSubject(E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.subject.AsyncSubject.subscribe.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.BehaviorSubject.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | BehaviorSubject.completed (rx.subject.BehaviorSubject.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

BehaviorSubject.completed

class BehaviorSubject(E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.subject.BehaviorSubject.subscribe.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.BehaviorSubject.this.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.BehaviorSubject.value.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.ReplaySubject.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | ReplaySubject.completed (rx.subject.ReplaySubject.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

ReplaySubject.completed

class ReplaySubject(E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.subject.ReplaySubject.subscribe.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.Subject.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Subject (rx.subject.Subject) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

Subject

Represents an object that is both an observable sequence as well as an observer.

interface Subject : Observer!E, Observable!E(
E
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.subject.SubjectObject.completed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SubjectObject.completed (rx.subject.SubjectObject.completed) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SubjectObject.completed

class SubjectObject(E)
void
completed
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.subject.SubjectObject.subscribe.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/rx.subject.SubjectObject.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | SubjectObject.this (rx.subject.SubjectObject.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

SubjectObject.this

class SubjectObject(E)
this
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.AtomicCounter.isZero.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AtomicCounter.isZero (rx.util.AtomicCounter.isZero) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AtomicCounter.isZero

class AtomicCounter
bool
isZero
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.AtomicCounter.tryDecrement.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AtomicCounter.tryDecrement (rx.util.AtomicCounter.tryDecrement) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AtomicCounter.tryDecrement

class AtomicCounter
tryDecrement
@trusted
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.AtomicCounter.trySetZero.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AtomicCounter.trySetZero (rx.util.AtomicCounter.trySetZero) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AtomicCounter.trySetZero

class AtomicCounter
bool
trySetZero
@trusted
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.AtomicCounter.tryUpdateCount.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | AtomicCounter.tryUpdateCount (rx.util.AtomicCounter.tryUpdateCount) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

AtomicCounter.tryUpdateCount

class AtomicCounter
bool
tryUpdateCount
@trusted
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.EventSignal.setSignal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | EventSignal.setSignal (rx.util.EventSignal.setSignal) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

EventSignal.setSignal

class EventSignal
void
setSignal
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.EventSignal.signal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | EventSignal.signal (rx.util.EventSignal.signal) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

EventSignal.signal

class EventSignal
bool
signal
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.EventSignal.this.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | EventSignal.this (rx.util.EventSignal.this) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

EventSignal.this

class EventSignal
this
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.EventSignal.wait.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | EventSignal.wait (rx.util.EventSignal.wait) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

EventSignal.wait

class EventSignal
void
wait
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.TicketBase.isStamped.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | TicketBase.isStamped (rx.util.TicketBase.isStamped) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

TicketBase.isStamped

class TicketBase
bool
isStamped
@property
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.TicketBase.stamp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | TicketBase.stamp (rx.util.TicketBase.stamp) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 37 |
38 |
39 |

TicketBase.stamp

class TicketBase
bool
stamp
(
)
40 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/rx.util.assumeThreadLocal.html: -------------------------------------------------------------------------------- 1 | Continue to overload -------------------------------------------------------------------------------- /docs/search-docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rx", 3 | "description": "Reactive Extensions for D Programming Language.", 4 | "copyright": "Copyright © 2015, lempiji", 5 | "license": "MIT", 6 | "homepage": "https://github.com/lempiji/rx", 7 | "authors": [ 8 | "lempiji" 9 | ], 10 | "sourcePaths": [ 11 | "source" 12 | ], 13 | "configurations": [ 14 | { 15 | "name": "default", 16 | "targetType": "library" 17 | }, 18 | { 19 | "name": "unittest", 20 | "dependencies": { 21 | "silly": "~>1.1.1" 22 | } 23 | }, 24 | { 25 | "name": "diff", 26 | "postGenerateCommands": [ 27 | "dub build -c makedeps -f", 28 | "dub fetch ddeps", 29 | "dub run ddeps -- --focus=rx --output=deps.dot", 30 | "dot -Tsvg -odeps.svg deps.dot", 31 | "dot -Tpng -odeps.png deps.dot" 32 | ] 33 | }, 34 | { 35 | "name": "diff-update", 36 | "postGenerateCommands": [ 37 | "dub fetch ddeps", 38 | "dub run ddeps -- --update" 39 | ] 40 | }, 41 | { 42 | "name": "makedeps", 43 | "dflags": ["-deps=deps.txt"] 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "silly": "1.1.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/concurrency/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | concurrency.so 6 | concurrency.dylib 7 | concurrency.dll 8 | concurrency.a 9 | concurrency.lib 10 | concurrency-test-* 11 | *.exe 12 | *.o 13 | *.obj 14 | *.lst 15 | -------------------------------------------------------------------------------- /examples/concurrency/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "concurrency", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "dependencies": { 7 | "rx": { "path": "../../" } 8 | } 9 | } -------------------------------------------------------------------------------- /examples/concurrency/source/app.d: -------------------------------------------------------------------------------- 1 | import rx; 2 | 3 | import core.time; 4 | import core.thread; 5 | 6 | import std.concurrency; 7 | import std.range : put; 8 | import std.stdio; 9 | 10 | void main() 11 | { 12 | auto timer = new ThreadTimer(1.seconds); 13 | auto counter = timer.scan!"a + 1"(0); // count up per 1sec 14 | 15 | auto receiver = counter.getReceiver(); 16 | scope (exit) 17 | receiver.dispose(); 18 | 19 | timer.start(); 20 | scope (exit) 21 | timer.stop(); 22 | 23 | auto n0 = receiver.receive(); 24 | writeln(n0); 25 | auto n1 = receiver.receive(); 26 | writeln(n1); 27 | auto n2 = receiver.receive(); 28 | writeln(n2); 29 | } 30 | 31 | auto getReceiver(TObservable)(auto ref TObservable observable) 32 | { 33 | auto tid = thisTid; 34 | auto disposable = observable.doSubscribe!((TObservable.ElementType elem) { 35 | send(tid, elem); 36 | }); 37 | 38 | static struct Receiver 39 | { 40 | typeof(disposable) _disposable; 41 | 42 | TObservable.ElementType receive() 43 | { 44 | return receiveOnly!(TObservable.ElementType); 45 | } 46 | 47 | void dispose() 48 | { 49 | _disposable.dispose(); 50 | } 51 | } 52 | 53 | return Receiver(disposable); 54 | } 55 | 56 | class ThreadTimer : SubjectObject!bool 57 | { 58 | Thread _thread; 59 | shared(bool) _shutdown; 60 | const(Duration) _interval; 61 | 62 | this(Duration interval) 63 | { 64 | _interval = interval; 65 | _thread = new Thread(&this.run); 66 | } 67 | 68 | void start() 69 | { 70 | _thread.start(); 71 | } 72 | 73 | void stop() 74 | in(_thread !is null) 75 | { 76 | _shutdown = true; 77 | } 78 | 79 | private void run() 80 | { 81 | if (!_shutdown) 82 | this.put(true); 83 | while (!_shutdown) 84 | { 85 | Thread.sleep(_interval); 86 | if (!_shutdown) 87 | this.put(true); 88 | } 89 | this.completed(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/fizzbuzz/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | -------------------------------------------------------------------------------- /examples/fizzbuzz/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fizzbuzz", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "description": "A example for rx.", 7 | "dependencies": { 8 | "rx": { 9 | "path": "../../" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /examples/fizzbuzz/source/app.d: -------------------------------------------------------------------------------- 1 | import std.conv; 2 | import std.range; 3 | import std.stdio; 4 | import rx; 5 | 6 | void main() 7 | { 8 | // 1) make source stream for 0 to 99 9 | auto sub = new SubjectObject!int; 10 | 11 | // 2) source map to Fizz or Buzz or numbers 12 | auto fizz = sub.filter!"a % 3 == 0"().map!(_ => "Fizz"); 13 | auto buzz = sub.filter!"a % 5 == 0"().map!(_ => "Buzz"); 14 | auto num = sub.filter!(a => a % 3 != 0 && a % 5 != 0).map!(to!string); 15 | auto tokens = fizz.merge(buzz).merge(num); 16 | 17 | // 3) source map to newline for delimiters 18 | auto newlines = sub.map!(_ => "\n"); 19 | 20 | // 4) merge tokens and newline 21 | // e.g. 22 | // 1 -> ( none , none , "1", "\n") -> write("1"); write("\n"); 23 | // 3 -> ("Fizz", none , none, "\n") -> write("Fizz"); write("\n"); 24 | // 5 -> ( none , "Buzz", none, "\n") -> write("Buzz"); write("\n"); 25 | // 15 -> ("Fizz", "Buzz", none, "\n") -> write("Fizz"); write("Buzz"); write("\n"); 26 | merge(tokens, newlines).doSubscribe!(token => write(token)); 27 | 28 | // 5) run with numbers 29 | .put(sub, iota(100)); 30 | } 31 | -------------------------------------------------------------------------------- /examples/http-client/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-client", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "description": "The example for an async http utilities", 7 | "copyright": "Copyright © 2017, lempiji", 8 | "license": "MIT", 9 | "dependencies": { 10 | "rx": { 11 | "path": "../../" 12 | } 13 | }, 14 | "libs": [ 15 | "curl" 16 | ] 17 | } -------------------------------------------------------------------------------- /examples/http-client/source/app.d: -------------------------------------------------------------------------------- 1 | import std.algorithm : splitter; 2 | import std.stdio; 3 | import std.range : put, take; 4 | import rx; 5 | 6 | void main() 7 | { 8 | auto signal = new EventSignal; 9 | auto client = getAsync("dlang.org"); 10 | 11 | // dfmt off 12 | client.map!(content => content.length) 13 | .doSubscribe((size_t length) { 14 | writeln("Content-Length: ", length); 15 | }, () { 16 | signal.setSignal(); 17 | }, (Exception e) { 18 | writeln(e); 19 | signal.setSignal(); 20 | }); 21 | // dfmt on 22 | 23 | signal.wait(); 24 | } 25 | 26 | Observable!(char[]) getAsync(const(char)[] url) 27 | { 28 | auto sub = new AsyncSubject!(char[]); 29 | 30 | import std.net.curl : HTTP, get; 31 | import std.parallelism : task, taskPool; 32 | 33 | taskPool.put(task({ 34 | auto http = HTTP(url); 35 | http.caInfo = "./curl-ca-bundle.crt"; 36 | 37 | try 38 | { 39 | .put(sub, get(url, http)); 40 | sub.completed(); 41 | } 42 | catch (Exception e) 43 | { 44 | sub.failure(e); 45 | } 46 | })); 47 | 48 | return sub; 49 | } 50 | 51 | auto getDefer(const(char)[] url) 52 | { 53 | return defer!(char[])((Observer!(char[]) observer) { 54 | import std.net.curl : HTTP, get; 55 | 56 | try 57 | { 58 | auto http = HTTP(url); 59 | http.caInfo = "./curl-ca-bundle.crt"; 60 | 61 | .put(observer, get(url, http)); 62 | observer.completed(); 63 | } 64 | catch (Exception e) 65 | { 66 | observer.failure(e); 67 | } 68 | 69 | return NopDisposable.instance; 70 | }).subscribeOn(new TaskPoolScheduler); 71 | } 72 | -------------------------------------------------------------------------------- /examples/mvvm-dlangui/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "copyright": "Copyright © 2016, lempiji", 6 | "dependencies": { 7 | "dlangui": "~>0.9.188", 8 | "rx": { 9 | "path": "../../" 10 | } 11 | }, 12 | "description": "The example for MVVM on Dlang UI", 13 | "license": "MIT", 14 | "name": "mvvm-dlangui" 15 | } -------------------------------------------------------------------------------- /examples/mvvm-dlangui/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import dlangui; 4 | import rx; 5 | 6 | import mvvm.common; 7 | import mvvm.models.simple; 8 | import mvvm.views.simple; 9 | 10 | mixin APP_ENTRY_POINT; 11 | 12 | /// entry point for dlangui based application 13 | extern (C) int UIAppMain(string[] args) 14 | { 15 | auto window = Platform.instance.createWindow("Sample Window", null); 16 | 17 | auto view = createSimpleView(); 18 | window.mainWidget = view; 19 | auto viewModel = new SimpleViewModel; 20 | 21 | // bind command and properties 22 | bindText(view.childById!EditLine("txtTitle"), viewModel.title); 23 | bindText(view.childById!TextWidget("lblTitle"), viewModel.title); 24 | bind(view.childById!SwitchButton("switchIsActive"), viewModel.isActive); 25 | bind(view.childById!Button("btnResetTitle"), viewModel.clearTitleCommand); 26 | 27 | // show window 28 | window.show(); 29 | 30 | // run message loop 31 | return Platform.instance.enterMessageLoop(); 32 | } 33 | -------------------------------------------------------------------------------- /examples/mvvm-dlangui/source/mvvm/models/simple.d: -------------------------------------------------------------------------------- 1 | module mvvm.models.simple; 2 | 3 | import std.stdio; 4 | import mvvm.common; 5 | 6 | class SimpleViewModel 7 | { 8 | private ReactiveProperty!string _title; 9 | public inout(ReactiveProperty!string) title() inout @property 10 | { 11 | return _title; 12 | } 13 | 14 | private ReactiveProperty!bool _isActive; 15 | public inout(ReactiveProperty!bool) isActive() inout @property 16 | { 17 | return _isActive; 18 | } 19 | 20 | private Command _clearTitleCommand; 21 | public inout(Command) clearTitleCommand() inout @property 22 | { 23 | return _clearTitleCommand; 24 | } 25 | 26 | this() 27 | { 28 | _title = new ReactiveProperty!string(""); 29 | _isActive = new ReactiveProperty!bool(false); 30 | 31 | _clearTitleCommand = new DelegateCommand(&clearTitle, isActive); 32 | } 33 | 34 | void clearTitle() 35 | { 36 | if (isActive.value) 37 | title.value = ""; 38 | } 39 | } 40 | 41 | unittest 42 | { 43 | auto model = new SimpleViewModel; 44 | assert(model.isActive.value == false); 45 | assert(model.title.value == ""); 46 | 47 | model.title.value = "ABC"; 48 | 49 | assert(model.title.value == "ABC"); 50 | model.clearTitle(); 51 | assert(model.title.value == "ABC"); 52 | 53 | model.isActive.value = true; 54 | model.clearTitle(); 55 | assert(model.title.value == ""); 56 | } 57 | -------------------------------------------------------------------------------- /examples/mvvm-dlangui/source/mvvm/views/simple.d: -------------------------------------------------------------------------------- 1 | module mvvm.views.simple; 2 | 3 | import dlangui; 4 | 5 | Widget createSimpleView() 6 | { 7 | return parseML(q{ 8 | VerticalLayout { 9 | EditLine { id: txtTitle; enabled: true } 10 | TextWidget { id: lblTitle } 11 | SwitchButton { id: switchIsActive } 12 | Button { id: btnResetTitle; text: "Reset" } 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /examples/mvvm-gtk-d/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /mvvm-gtk-d 6 | mvvm-gtk-d.so 7 | mvvm-gtk-d.dylib 8 | mvvm-gtk-d.dll 9 | mvvm-gtk-d.a 10 | mvvm-gtk-d.lib 11 | mvvm-gtk-d-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /examples/mvvm-gtk-d/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "dependencies": { 6 | "gtk-d": "~>3.10.0", 7 | "rx": { 8 | "path": "../../" 9 | } 10 | }, 11 | "description": "The example for MVVM on Gtk-D", 12 | "license": "MIT", 13 | "name": "mvvm-gtk-d" 14 | } -------------------------------------------------------------------------------- /examples/mvvm-gtk-d/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import gtk.Main; 4 | import gtk.Widget; 5 | 6 | import mvvm.model; 7 | import mvvm.view; 8 | 9 | void main(string[] args) 10 | { 11 | Main.init(args); 12 | 13 | auto viewModel = new AppViewModel; 14 | auto window = new MyAppWindow(viewModel); 15 | window.addOnDestroy((Widget _) { Main.quit(); }); 16 | 17 | window.showAll(); 18 | 19 | Main.run(); 20 | } 21 | -------------------------------------------------------------------------------- /examples/mvvm-gtk-d/source/mvvm/util.d: -------------------------------------------------------------------------------- 1 | module mvvm.util; 2 | 3 | import std.range : put; 4 | 5 | import gobject.Signals; 6 | 7 | import gtk.Button; 8 | import gtk.Entry; 9 | import gtk.Label; 10 | 11 | import rx; 12 | 13 | Entry toBindedEntry(Subject!string source, CompositeDisposable bag) 14 | { 15 | auto entry = new Entry; 16 | auto handleId = entry.addOnChanged(_ => .put(source, entry.getText())); 17 | bag.insert(new AnonymousDisposable({ 18 | Signals.handlerDisconnect(entry, handleId); 19 | })); 20 | bag.insert(source.doSubscribe((string text) { entry.setText(text); })); 21 | return entry; 22 | } 23 | 24 | Label toBindedLabel(TObservable)(auto ref TObservable source, CompositeDisposable bag) 25 | { 26 | import std.conv; 27 | 28 | auto label = new Label(""); 29 | bag.insert(source.doSubscribe((TObservable.ElementType obj) { 30 | label.setText(obj.to!string); 31 | })); 32 | return label; 33 | } 34 | 35 | Button makeBindedButton(string text, void delegate() onClick, Observable!bool sensitiveSource, CompositeDisposable disposeBag) 36 | in 37 | { 38 | const hasSensitiveSource = sensitiveSource !is null; 39 | const hasDisposeBag = disposeBag !is null; 40 | if (hasSensitiveSource) 41 | assert(hasDisposeBag); 42 | } 43 | do 44 | { 45 | auto button = new Button(text); 46 | button.addOnClicked((Button _) { onClick(); }); 47 | if (sensitiveSource !is null) 48 | { 49 | disposeBag.insert(sensitiveSource.doSubscribe(&button.setSensitive)); 50 | } 51 | return button; 52 | } 53 | 54 | Button makeBindedButton(string text, void delegate() onClick, CompositeDisposable disposeBag) 55 | { 56 | return makeBindedButton(text, onClick, null, disposeBag); 57 | } 58 | -------------------------------------------------------------------------------- /examples/mvvm-gtk-d/source/mvvm/view.d: -------------------------------------------------------------------------------- 1 | module mvvm.view; 2 | 3 | import core.time; 4 | 5 | import gtk.MainWindow; 6 | import gtk.Box; 7 | import gtk.Entry; 8 | import gtk.Label; 9 | import gtk.Button; 10 | import gtk.Widget; 11 | 12 | import rx; 13 | 14 | import mvvm.model; 15 | import mvvm.util; 16 | 17 | class MyAppWindow : MainWindow 18 | { 19 | this(AppViewModel model) 20 | { 21 | super("rx example"); 22 | setDefaultSize(640, 480); 23 | 24 | auto disposeBag = new CompositeDisposable; 25 | this.addOnDestroy((Widget _) { disposeBag.dispose(); }); 26 | 27 | auto name = model.name.toBindedEntry(disposeBag); 28 | auto age = model.age.toBindedLabel(disposeBag); 29 | auto profile = model.profile.debounce(500.msecs).toBindedLabel(disposeBag); 30 | auto incrementAge = makeBindedButton("+1", &model.incrementAge, disposeBag); 31 | auto decrementAge = makeBindedButton("-1", &model.decrementAge, 32 | model.canDecrementAge, disposeBag); 33 | auto clear = makeBindedButton("Clear", { 34 | model.clear(); 35 | profile.setText(""); 36 | }, model.canClear, disposeBag); 37 | 38 | auto box = new Box(GtkOrientation.VERTICAL, 5); 39 | box.packStart(makeLine("Name:", name), false, false, 0); 40 | box.packStart(makeLine("Age:", age, decrementAge, incrementAge), false, false, 0); 41 | box.packStart(makeLine("Profile:", profile), false, false, 0); 42 | box.packStart(clear, false, false, 0); 43 | add(box); 44 | } 45 | } 46 | 47 | Box makeLine(string name, Widget[] widgets...) 48 | { 49 | auto box = new Box(GtkOrientation.HORIZONTAL, 5); 50 | box.packStart(new Label(name), false, false, 0); 51 | foreach (widget; widgets) 52 | box.packStart(widget, false, false, 0); 53 | return box; 54 | } 55 | -------------------------------------------------------------------------------- /examples/overview/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overview", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "description": "A example for rx.", 7 | "dependencies": { 8 | "rx": { 9 | "path": "../../" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /examples/overview/source/app.d: -------------------------------------------------------------------------------- 1 | import rx; 2 | import std.conv : to; 3 | import std.range : iota, put; 4 | 5 | void main() 6 | { 7 | // create own source of int 8 | auto subject = new SubjectObject!int; 9 | 10 | // define result array 11 | string[] result; 12 | 13 | // define pipeline and subscribe 14 | // sequence: source -> filter by even -> map to string -> join to result 15 | auto disposable = subject.filter!(n => n % 2 == 0).map!(o => to!string(o)) 16 | .doSubscribe!(text => result ~= text); 17 | 18 | // set unsubscribe on exit 19 | // it is not necessary in this simple example, 20 | // but in many cases you should call dispose to prevent memory leaks. 21 | scope (exit) 22 | disposable.dispose(); 23 | 24 | // put values to source. 25 | put(subject, iota(10)); 26 | 27 | // result is like this 28 | assert(result == ["0", "2", "4", "6", "8"]); 29 | } 30 | -------------------------------------------------------------------------------- /examples/rx-dlangui/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "copyright": "Copyright © 2016, lempiji", 6 | "dependencies": { 7 | "dlangui": "~>0.9.188", 8 | "rx": { 9 | "path": "../../" 10 | } 11 | }, 12 | "description": "The example for rx with Dlang UI.", 13 | "license": "public domain", 14 | "name": "rx-dlangui" 15 | } -------------------------------------------------------------------------------- /examples/rx-dlangui/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import dlangui; 4 | import rx; 5 | import std.conv; 6 | 7 | //main of this sample 8 | import event; 9 | import model; 10 | 11 | mixin APP_ENTRY_POINT; 12 | 13 | /// entry point for dlangui based application 14 | extern (C) int UIAppMain(string[] args) 15 | { 16 | auto window = createAppWindow(); 17 | auto appModel = new MyModel; 18 | 19 | auto counter = window.mainWidget.childById!EditLine("counter"); 20 | 21 | appModel.counter.doSubscribe((int n) { counter.text = to!dstring(n); }); 22 | counter.text = to!dstring(appModel.count); 23 | 24 | Disposable[] events; 25 | events ~= window.mainWidget.childById!Button("btnIncrement") 26 | .click.asObservable().doSubscribe((Widget _) { appModel.increment(); }); 27 | events ~= window.mainWidget.childById!Button("btnDecrement") 28 | .click.asObservable().doSubscribe((Widget _) { appModel.decrement(); }); 29 | 30 | window.mainWidget.childById!Button("btnDetach").click = (Widget _) { 31 | foreach (e; events) 32 | e.dispose(); 33 | return true; 34 | }; 35 | 36 | // close window on Close button click 37 | window.mainWidget.childById!Button("btnClose").click = delegate(Widget _) { 38 | window.close(); 39 | return true; 40 | }; 41 | 42 | // show window 43 | window.show(); 44 | 45 | // run message loop 46 | return Platform.instance.enterMessageLoop(); 47 | } 48 | 49 | Window createAppWindow() 50 | { 51 | // create window 52 | Log.d("Creating window"); 53 | if (!Platform.instance) 54 | { 55 | Log.e("Platform.instance is null!!!"); 56 | } 57 | 58 | auto window = Platform.instance.createWindow("DlangUI with Rx", null); 59 | Log.d("Window created"); 60 | 61 | // create some widget to show in window 62 | window.mainWidget = parseML(q{ 63 | VerticalLayout { 64 | padding: 10 65 | layoutWidth: fill 66 | backgroundColor: "#C0E0E070" // semitransparent yellow background 67 | 68 | // red bold text with size = 150% of base style size and font face Arial 69 | TextWidget { text: "The example for Rx on DlangUI"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "'Yu Gothic',Arial" } 70 | 71 | // arrange some checkboxes horizontally 72 | HorizontalLayout { 73 | layoutWidth: fill 74 | 75 | TextWidget { text: "Counter" } 76 | EditLine { id: counter; text: "some text"; layoutWidth: fill } 77 | 78 | Button { id: btnIncrement; text: "+1" } 79 | Button { id: btnDecrement; text: "-1" } 80 | } 81 | 82 | Button { id: btnDetach; text: "Detach" } 83 | 84 | Button { id: btnClose; text: "Close" } 85 | } 86 | }); 87 | 88 | return window; 89 | } 90 | -------------------------------------------------------------------------------- /examples/rx-dlangui/source/event.d: -------------------------------------------------------------------------------- 1 | module event; 2 | 3 | import std.traits; 4 | import rx; 5 | import dlangui.core.signals; 6 | 7 | ///Wrap a Signal!T as Observable 8 | auto asObservable(T)(ref T signal) if (is(T == Signal!U, U) && is(U == interface)) 9 | { 10 | static if (is(T == Signal!U, U)) 11 | { 12 | alias return_t = ReturnType!(__traits(getMember, U, __traits(allMembers, U)[0])); 13 | alias param_t = ParameterTypeTuple!(__traits(getMember, U, __traits(allMembers, U)[0])); 14 | static assert(param_t.length == 1); 15 | } 16 | 17 | static struct LocalObservable 18 | { 19 | alias ElementType = param_t[0]; 20 | this(ref T signal) 21 | { 22 | _subscribe = (Observer!ElementType o) { 23 | auto dg = (ElementType w) { 24 | o.put(w); 25 | static if (is(return_t == bool)) 26 | { 27 | return true; 28 | } 29 | }; 30 | 31 | signal.connect(dg); 32 | 33 | return new AnonymousDisposable({ signal.disconnect(dg); }); 34 | }; 35 | } 36 | 37 | auto subscribe(U)(U observer) 38 | { 39 | return _subscribe(observerObject!ElementType(observer)); 40 | } 41 | 42 | Disposable delegate(Observer!ElementType) _subscribe; 43 | } 44 | 45 | return LocalObservable(signal); 46 | } 47 | -------------------------------------------------------------------------------- /examples/rx-dlangui/source/model.d: -------------------------------------------------------------------------------- 1 | module model; 2 | 3 | import rx; 4 | 5 | class MyModel 6 | { 7 | public: 8 | this() 9 | { 10 | _count = 0; 11 | _counter = new SubjectObject!int; 12 | } 13 | 14 | public: 15 | int count() @property 16 | { 17 | return _count; 18 | } 19 | 20 | Observable!int counter() @property 21 | { 22 | return _counter; 23 | } 24 | 25 | public: 26 | void increment() 27 | { 28 | _count++; 29 | _counter.put(_count); 30 | } 31 | 32 | void decrement() 33 | { 34 | _count--; 35 | _counter.put(_count); 36 | } 37 | 38 | private: 39 | int _count; 40 | SubjectObject!int _counter; 41 | } 42 | -------------------------------------------------------------------------------- /examples/rx-dlangui/source/win_app.def: -------------------------------------------------------------------------------- 1 | EXETYPE NT 2 | SUBSYSTEM WINDOWS -------------------------------------------------------------------------------- /examples/rx-dnd-gtkd/dub.sdl: -------------------------------------------------------------------------------- 1 | name "rx-dnd-gtkd" 2 | description "A minimal D application." 3 | authors "lempiji" 4 | dependency "rx" path="../../" 5 | dependency "gfm:math" version="~>8.0.6" 6 | dependency "gtk-d" version="~>3.10.0" 7 | -------------------------------------------------------------------------------- /examples/rx-fswatch/data/data.txt: -------------------------------------------------------------------------------- 1 | TEST2 -------------------------------------------------------------------------------- /examples/rx-fswatch/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rx-fswatch", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "description": "A minimal D application.", 7 | "dependencies": { 8 | "fswatch": "*", 9 | "rx": { 10 | "path": "../../" 11 | } 12 | }, 13 | "license": "MIT" 14 | } -------------------------------------------------------------------------------- /examples/rx-fswatch/dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "fswatch": "0.6.0", 5 | "rx": {"path":"../../"}, 6 | "silly": "1.1.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/rx-fswatch/source/app.d: -------------------------------------------------------------------------------- 1 | import core.thread; 2 | import core.time; 3 | import fswatch; 4 | import rx; 5 | import std.range : put; 6 | import std.stdio; 7 | 8 | void main() 9 | { 10 | auto watcher = defer!(FileChangeEvent[], (observer) { 11 | auto shutdown = false; 12 | 13 | auto thread = new Thread({ 14 | try 15 | { 16 | auto watch = FileWatch("./data", true); 17 | 18 | while (!shutdown) 19 | { 20 | auto events = watch.getEvents(); 21 | if (events.length > 0) 22 | { 23 | .put(observer, events); 24 | } 25 | 26 | Thread.sleep(100.msecs); 27 | } 28 | observer.completed(); 29 | } 30 | catch (Exception e) 31 | { 32 | observer.failure(e); 33 | } 34 | }); 35 | thread.start(); 36 | 37 | return new AnonymousDisposable({ shutdown = true; }); 38 | }); 39 | 40 | auto flatten = watcher.map!(events => from(events)).merge(); 41 | auto changes = flatten.groupBy!(event => event.path) 42 | .map!(o => o.debounce(2.seconds)) 43 | .merge(); 44 | 45 | // start FileWatch 46 | auto disposable = changes.doSubscribe((FileChangeEvent event) { 47 | writeln(event); 48 | }); 49 | 50 | scope (exit) 51 | disposable.dispose(); 52 | 53 | writeln("Plaese Enter to exit."); 54 | readln(); 55 | } 56 | -------------------------------------------------------------------------------- /examples/rx-gtk-d/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rx-gtk-d", 3 | "authors": [ 4 | "lempiji" 5 | ], 6 | "description": "A minimal D application.", 7 | "copyright": "Copyright © 2017, lempiji", 8 | "license": "MIT", 9 | "dependencies": { 10 | "rx": { 11 | "path": "../../" 12 | }, 13 | "gtk-d:gtkd": "~>3.10.0" 14 | } 15 | } -------------------------------------------------------------------------------- /examples/rx-gtk-d/source/app.d: -------------------------------------------------------------------------------- 1 | import gio.Application : GioApplication = Application; 2 | import gtk.Application; 3 | import gtk.ApplicationWindow; 4 | import gtk.EventBox; 5 | import gtk.Entry; 6 | import gtk.Label; 7 | import gtk.Widget; 8 | import gtk.Button; 9 | import gtk.VBox; 10 | 11 | import core.time; 12 | import rx; 13 | import std.range : put; 14 | 15 | class MyApplication : ApplicationWindow 16 | { 17 | this(Application application) 18 | { 19 | super(application); 20 | setTitle("rx: Example"); 21 | setDefaultSize(640, 480); 22 | 23 | auto vbox = new VBox(false, 0); 24 | auto desc = new Label("Label synchronize to Entry with delay"); 25 | auto input = new Entry(); 26 | auto noDelay = new Label(""); 27 | auto delayed = new Label(""); 28 | auto clearButton = new Button("Clear"); 29 | auto detachButton = new Button("Detach"); 30 | 31 | vbox.packStart(desc, false, false, 0); 32 | vbox.packStart(input, false, false, 0); 33 | vbox.packStart(noDelay, false, false, 0); 34 | vbox.packStart(delayed, false, false, 0); 35 | vbox.packStart(clearButton, false, false, 0); 36 | vbox.packStart(detachButton, false, false, 0); 37 | add(vbox); 38 | 39 | //Create two Observable, and delay one. 40 | auto changedText = input.changedAsObservable().map!(entry => entry.getText()); 41 | auto delayedText = changedText.debounce(300.msecs); 42 | 43 | //Subscribe in the same way. 44 | auto d1 = changedText.doSubscribe((string text) { noDelay.setText(text); }); 45 | auto d2 = delayedText.doSubscribe((string text) { delayed.setText(text); }); 46 | 47 | clearButton.addOnClicked((Button _) { input.setText(""); }); 48 | detachButton.addOnClicked((Button _) { d1.dispose(); d2.dispose(); }); 49 | 50 | showAll(); 51 | } 52 | } 53 | 54 | int main(string[] args) 55 | { 56 | auto application = new Application("rx.myapplication", GApplicationFlags.FLAGS_NONE); 57 | application.addOnActivate(delegate void(GioApplication app) { 58 | new MyApplication(application); 59 | }); 60 | return application.run(args); 61 | } 62 | 63 | Observable!Entry changedAsObservable(Entry entry) 64 | { 65 | import gobject.Signals; 66 | 67 | return defer!Entry((Observer!Entry observer) { 68 | auto handleId = entry.addOnChanged(_ => .put(observer, entry)); 69 | return new AnonymousDisposable({ 70 | Signals.handlerDisconnect(entry, handleId); 71 | }); 72 | }).observableObject!Entry(); 73 | } 74 | -------------------------------------------------------------------------------- /examples/scheduler-dlangui/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "copyright": "Copyright © 2016, lempiji", 6 | "dependencies": { 7 | "dlangui": "~>0.9.188", 8 | "rx": { 9 | "path": "../../", 10 | "version": ">=0.0.0" 11 | } 12 | }, 13 | "description": "A minimal D application.", 14 | "license": "MIT", 15 | "name": "scheduler-dlangui" 16 | } -------------------------------------------------------------------------------- /examples/scheduler-dlangui/source/app.d: -------------------------------------------------------------------------------- 1 | import core.time; 2 | import std.datetime; 3 | import std.conv; 4 | import std.stdio; 5 | 6 | import dlangui; 7 | import rx; 8 | 9 | import utils; 10 | 11 | mixin APP_ENTRY_POINT; 12 | 13 | /// entry point for dlangui based application 14 | extern (C) int UIAppMain(string[] args) 15 | { 16 | auto window = createAppWindow(); 17 | 18 | auto scheduler = new DlangUIScheduler(); 19 | window.mainWidget.addChild(scheduler); 20 | currentScheduler = scheduler; 21 | 22 | auto label = window.mainWidget.childById!TextWidget("label"); 23 | auto edit = window.mainWidget.childById!EditLine("edit"); 24 | 25 | edit.contentChange.asObservable().debounce(dur!"msecs"(200)).doSubscribe((EditableContent _) { 26 | label.text = edit.text; 27 | }); 28 | 29 | // show window 30 | window.show(); 31 | 32 | // run message loop 33 | return Platform.instance.enterMessageLoop(); 34 | } 35 | 36 | Window createAppWindow() 37 | { 38 | // create window 39 | Log.d("Creating window"); 40 | if (!Platform.instance) 41 | { 42 | Log.e("Platform.instance is null!!!"); 43 | } 44 | 45 | auto window = Platform.instance.createWindow("DlangUI with Rx", null); 46 | Log.d("Window created"); 47 | 48 | // create some widget to show in window 49 | window.mainWidget = parseML(q{ 50 | VerticalLayout { 51 | padding: 10 52 | layoutWidth: fill 53 | backgroundColor: "#C0E0E070" // semitransparent yellow background 54 | 55 | EditLine { id: edit; layoutWidth: fill } 56 | TextWidget { id: label; } 57 | } 58 | }); 59 | 60 | return window; 61 | } 62 | -------------------------------------------------------------------------------- /examples/scheduler-dlangui/source/utils.d: -------------------------------------------------------------------------------- 1 | module utils; 2 | 3 | import core.time; 4 | import std.traits; 5 | import rx; 6 | import dlangui.core.signals; 7 | import dlangui.widgets.widget; 8 | 9 | ///Wrap a Signal!T as Observable 10 | auto asObservable(T)(ref T signal) if (is(T == Signal!U, U) && is(U == interface)) 11 | { 12 | static if (is(T == Signal!U, U)) 13 | { 14 | alias return_t = ReturnType!(__traits(getMember, U, __traits(allMembers, U)[0])); 15 | alias param_t = ParameterTypeTuple!(__traits(getMember, U, __traits(allMembers, U)[0])); 16 | static assert(param_t.length == 1); 17 | } 18 | 19 | static struct LocalObservable 20 | { 21 | alias ElementType = param_t[0]; 22 | this(ref T signal) 23 | { 24 | _subscribe = (Observer!ElementType o) { 25 | auto dg = (ElementType w) { 26 | o.put(w); 27 | static if (is(return_t == bool)) 28 | { 29 | return true; 30 | } 31 | }; 32 | 33 | signal.connect(dg); 34 | 35 | return new AnonymousDisposable({ signal.disconnect(dg); }); 36 | }; 37 | } 38 | 39 | auto subscribe(U)(U observer) 40 | { 41 | return _subscribe(observerObject!ElementType(observer)); 42 | } 43 | 44 | Disposable delegate(Observer!ElementType) _subscribe; 45 | } 46 | 47 | return LocalObservable(signal); 48 | } 49 | 50 | struct TimerHandler 51 | { 52 | CancellationToken token; 53 | void delegate() action; 54 | } 55 | 56 | class DlangUIScheduler : Widget, Scheduler 57 | { 58 | TimerHandler[ulong] _actions; 59 | Object _gate = new Object(); 60 | ulong _timerId; 61 | 62 | CancellationToken schedule(void delegate() op, Duration val) 63 | { 64 | auto ms = val.total!"msecs"; 65 | auto id = setTimer(ms); 66 | auto token = new CancellationToken(); 67 | synchronized (_gate) 68 | { 69 | _actions[id] = TimerHandler(token, op); 70 | } 71 | return token; 72 | } 73 | 74 | override bool onTimer(ulong id) 75 | { 76 | CancellationToken token; 77 | void delegate() action; 78 | 79 | synchronized (_gate) 80 | { 81 | auto temp = id in _actions; 82 | if (temp) 83 | { 84 | token = (*temp).token; 85 | action = (*temp).action; 86 | 87 | _actions.remove(id); 88 | } 89 | else 90 | { 91 | return false; 92 | } 93 | } 94 | 95 | if (token is null || !token.isCanceled) 96 | { 97 | action(); 98 | return true; 99 | } 100 | 101 | return false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/scheduler-dlangui/source/win_app.def: -------------------------------------------------------------------------------- 1 | EXETYPE NT 2 | SUBSYSTEM WINDOWS -------------------------------------------------------------------------------- /source/rx/algorithm/fold.d: -------------------------------------------------------------------------------- 1 | /+++++++++++++++++++++++++++++ 2 | + This module defines algorithm 'fold' 3 | +/ 4 | module rx.algorithm.fold; 5 | 6 | import rx.disposable; 7 | import rx.observable; 8 | import rx.observer; 9 | 10 | //#################### 11 | // Fold 12 | //#################### 13 | /// 14 | auto fold(alias fun, TObservable, Seed)(auto ref TObservable observable, Seed seed) 15 | { 16 | import rx.algorithm : scan; 17 | import rx.range : takeLast; 18 | 19 | return observable.scan!fun(seed).takeLast; 20 | } 21 | /// 22 | unittest 23 | { 24 | import rx.subject : SubjectObject; 25 | 26 | auto sub = new SubjectObject!int; 27 | auto sum = sub.fold!"a+b"(0); 28 | 29 | int result = 0; 30 | auto disposable = sum.doSubscribe((int n) { result = n; }); 31 | scope (exit) 32 | disposable.dispose(); 33 | 34 | foreach (i; 1 .. 11) 35 | sub.put(i); 36 | 37 | assert(result == 0); 38 | sub.completed(); 39 | assert(result == 55); 40 | } 41 | -------------------------------------------------------------------------------- /source/rx/algorithm/package.d: -------------------------------------------------------------------------------- 1 | module rx.algorithm; 2 | 3 | public import rx.algorithm.all; 4 | public import rx.algorithm.any; 5 | public import rx.algorithm.buffer; 6 | public import rx.algorithm.combineLatest; 7 | public import rx.algorithm.debounce; 8 | public import rx.algorithm.filter; 9 | public import rx.algorithm.fold; 10 | public import rx.algorithm.groupby; 11 | public import rx.algorithm.map; 12 | public import rx.algorithm.merge; 13 | public import rx.algorithm.scan; 14 | public import rx.algorithm.tee; 15 | public import rx.algorithm.uniq; 16 | 17 | //#################### 18 | // Overview 19 | //#################### 20 | /// 21 | unittest 22 | { 23 | import rx; 24 | import std.conv : to; 25 | import std.range : iota, put; 26 | 27 | auto subject = new SubjectObject!int; 28 | 29 | string[] result; 30 | auto disposable = subject.filter!(n => n % 2 == 0).map!(o => to!string(o)) 31 | .doSubscribe!(text => result ~= text); 32 | 33 | scope (exit) 34 | disposable.dispose(); 35 | 36 | put(subject, iota(10)); 37 | 38 | assert(result == ["0", "2", "4", "6", "8"]); 39 | } 40 | 41 | /// 42 | unittest 43 | { 44 | import rx; 45 | 46 | auto sub = new SubjectObject!int; 47 | 48 | auto hasEven = sub.any!"a % 2 == 0"(); 49 | auto result = false; 50 | auto disposable = hasEven.doSubscribe((bool b) { result = b; }); 51 | scope (exit) 52 | disposable.dispose(); 53 | 54 | sub.put(1); 55 | sub.put(3); 56 | sub.put(2); 57 | assert(result); 58 | } 59 | -------------------------------------------------------------------------------- /source/rx/package.d: -------------------------------------------------------------------------------- 1 | module rx; 2 | 3 | public import rx.disposable; 4 | public import rx.observer; 5 | public import rx.observable; 6 | public import rx.subject; 7 | public import rx.scheduler; 8 | public import rx.range; 9 | public import rx.algorithm; 10 | public import rx.util; 11 | -------------------------------------------------------------------------------- /source/rx/range/package.d: -------------------------------------------------------------------------------- 1 | module rx.range; 2 | 3 | public import rx.range.drop; 4 | public import rx.range.take; 5 | public import rx.range.takeLast; 6 | public import rx.range.takeUntil; 7 | public import rx.range.zip; 8 | 9 | /+++++++++++++++++++++++++++++ 10 | + Overview 11 | +/ 12 | unittest 13 | { 14 | import rx : SubjectObject, observerObject, drop, take; 15 | import std.algorithm : equal; 16 | import std.array : appender; 17 | import std.conv : to; 18 | 19 | auto subject = new SubjectObject!int; 20 | auto pub = subject.drop(2).take(3); 21 | 22 | auto buf = appender!(int[]); 23 | auto disposable = pub.subscribe(observerObject!int(buf)); 24 | 25 | foreach (i; 0 .. 10) 26 | { 27 | subject.put(i); 28 | } 29 | 30 | auto result = buf.data; 31 | assert(equal(result, [2, 3, 4])); 32 | } 33 | -------------------------------------------------------------------------------- /source/rx/range/takeUntil.d: -------------------------------------------------------------------------------- 1 | /+++++++++++++++++++++++++++++ 2 | + This module is a submodule of rx.range. 3 | + It provides basic operation a 'takeUntil' 4 | +/ 5 | module rx.range.takeUntil; 6 | 7 | import rx.disposable; 8 | import rx.observer; 9 | import rx.observable; 10 | import rx.util; 11 | 12 | import std.range : put; 13 | 14 | //#################### 15 | // TakeUntil 16 | //#################### 17 | /// 18 | auto takeUntil(TObservable1, TObservable2)(auto ref TObservable1 source, auto ref TObservable2 stopper) 19 | if (isObservable!TObservable1 && isObservable!TObservable2) 20 | { 21 | static struct TakeUntilObservable 22 | { 23 | alias ElementType = TObservable1.ElementType; 24 | 25 | TObservable1 source; 26 | TObservable2 stopper; 27 | 28 | auto subscribe(TObserver)(auto ref TObserver observer) 29 | { 30 | auto sourceDisposable = source.doSubscribe(observer); 31 | auto stopperDisposable = stopper.doSubscribe((TObservable2.ElementType _) { 32 | static if (hasCompleted!TObserver) 33 | { 34 | observer.completed(); 35 | } 36 | sourceDisposable.dispose(); 37 | }); 38 | 39 | return new CompositeDisposable(sourceDisposable, stopperDisposable); 40 | } 41 | } 42 | 43 | return TakeUntilObservable(source, stopper); 44 | } 45 | 46 | /// 47 | unittest 48 | { 49 | import std.algorithm; 50 | import rx; 51 | 52 | auto source = new SubjectObject!int; 53 | auto stopper = new SubjectObject!int; 54 | 55 | int[] buf; 56 | auto disposable = source.takeUntil(stopper).doSubscribe!((n) { buf ~= n; }); 57 | 58 | source.put(0); 59 | source.put(1); 60 | source.put(2); 61 | 62 | stopper.put(0); 63 | 64 | source.put(3); 65 | source.put(4); 66 | 67 | assert(equal(buf, [0, 1, 2])); 68 | } 69 | 70 | unittest 71 | { 72 | import std.algorithm; 73 | import rx; 74 | 75 | auto sub1 = new SubjectObject!int; 76 | auto sub2 = new SubjectObject!int; 77 | 78 | int[] buf; 79 | auto disposable = sub1.takeUntil(sub2).subscribe((int n) { buf ~= n; }); 80 | 81 | sub1.put(0); 82 | 83 | disposable.dispose(); 84 | 85 | sub1.put(1); 86 | 87 | assert(equal(buf, [0])); 88 | } 89 | --------------------------------------------------------------------------------