Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Implementing Observables shim, issue #59 #62

Merged
merged 3 commits into from
Dec 7, 2016
Merged

Conversation

rorticus
Copy link
Contributor

@rorticus rorticus commented Dec 5, 2016

Type: feature

Description:

Shim implementation of ES observables spec. Non-spec functionality (i.e., toPromise, map, filter) will go in core.

Related Issue: #59

Please review this checklist before submitting your PR:

  • There is a related issue
  • All contributors have signed a CLA
  • All code matches the style guide
  • The code passes the CI tests
  • Unit or Functional tests are included in the PR
  • The PR increases or maintains the overall unit test coverage percentage
  • The code is ready to be merged

@rorticus
Copy link
Contributor Author

rorticus commented Dec 5, 2016

Also, I should note.. I talked with @matt-gadd about the fact that our interface calls a property isUnsubscribed and the spec calls it closed. We decided to go with the spec, knowing that there are going to be some breaking changes.

@codecov-io
Copy link

codecov-io commented Dec 5, 2016

Current coverage is 97.04% (diff: 100%)

Merging #62 into master will increase coverage by 0.41%

@@             master        #62   diff @@
==========================================
  Files            29         30     +1   
  Lines          1129       1287   +158   
  Methods          23         24     +1   
  Messages          0          0          
  Branches        211        248    +37   
==========================================
+ Hits           1091       1249   +158   
  Misses           21         21          
  Partials         17         17          

Powered by Codecov. Last update a4089d9...60aa935

@dylans dylans added this to the 2016.12 milestone Dec 5, 2016
@rorticus
Copy link
Contributor Author

rorticus commented Dec 5, 2016

OK don't review this just yet.. I ran against the tests that Ant mentioned and I've got some things to fix up first :)

@rorticus
Copy link
Contributor Author

rorticus commented Dec 6, 2016

OK. Refactored quite a bit... All the ES spec tests pass now!

[90mStarting tests...[39m

[1mObservable constructor[22m

[1m  Argument types[22m
    The first argument cannot be a non-callable object [1m[32mOK[39m[22m
    The first argument cannot be a primative value [1m[32mOK[39m[22m
    The first argument cannot be a primative value [1m[32mOK[39m[22m
    The first argument cannot be a primative value [1m[32mOK[39m[22m
    The first argument cannot be a primative value [1m[32mOK[39m[22m
    The first argument can be a function [1m[32mOK[39m[22m

[1m  Observable.prototype has a constructor property[22m
    Property constructor exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m
    Observable.prototype.constructor === Observable [1m[32mOK[39m[22m

[1m  Subscriber function is not called by constructor[22m
    The constructor does not call the subscriber function [1m[32mOK[39m[22m

[1mObservable.prototype.subscribe[22m

[1m  Observable.prototype has a subscribe property[22m
    Property subscribe exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Argument type[22m
    Throws if observer is not an object [1m[32mOK[39m[22m
    Throws if observer is not an object [1m[32mOK[39m[22m
    Throws if observer is not an object [1m[32mOK[39m[22m
    Throws if observer is not an object [1m[32mOK[39m[22m
    Throws if observer is not an object [1m[32mOK[39m[22m
    Any object may be an observer [1m[32mOK[39m[22m
    Any object may be an observer [1m[32mOK[39m[22m
    Any object may be an observer [1m[32mOK[39m[22m

[1m  Function arguments[22m
    First argument is next callback [1m[32mOK[39m[22m
    Second argument is error callback [1m[32mOK[39m[22m
    Third argument is complete callback [1m[32mOK[39m[22m
    Second and third arguments are optional [1m[32mOK[39m[22m
    Second and third arguments are optional [1m[32mOK[39m[22m

[1m  Subscriber arguments[22m
    Subscriber is called with an observer [1m[32mOK[39m[22m
    Subscriber is called with an observer [1m[32mOK[39m[22m
    Subscriber is called with an observer [1m[32mOK[39m[22m
    Subscriber is called with an observer [1m[32mOK[39m[22m
    Subscription observer's constructor property is Object [1m[32mOK[39m[22m

[1m  Subscriber return types[22m
    Undefined can be returned [1m[32mOK[39m[22m
    Null can be returned [1m[32mOK[39m[22m
    Functions can be returned [1m[32mOK[39m[22m
    Subscriptions can be returned [1m[32mOK[39m[22m
    Non callable, non-subscription objects cannot be returned [1m[32mOK[39m[22m
    Non-functions cannot be returned [1m[32mOK[39m[22m
    Non-functions cannot be returned [1m[32mOK[39m[22m

[1m  Returns a subscription object[22m
    Property unsubscribe exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 0 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m
    Property closed exists on the object [1m[32mOK[39m[22m
    Property has a getter [1m[32mOK[39m[22m
    Function length is 0 [1m[32mOK[39m[22m
    Property does not have a setter [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m
    Subscribe returns an object [1m[32mOK[39m[22m
    Contructor property is Object [1m[32mOK[39m[22m
    closed property returns false before unsubscription [1m[32mOK[39m[22m
    Unsubscribe returns undefined [1m[32mOK[39m[22m
    Unsubscribe calls the cleanup function [1m[32mOK[39m[22m
    closed property is true after calling unsubscribe [1m[32mOK[39m[22m

[1m  Cleanup function[22m
    The cleanup function is called when unsubscribing [1m[32mOK[39m[22m
    The cleanup function is not called again when unsubscribe is called again [1m[32mOK[39m[22m
    The cleanup function is called when an error is sent to the sink [1m[32mOK[39m[22m
    The cleanup function is called when a complete is sent to the sink [1m[32mOK[39m[22m
    If a subscription is returned, then unsubscribe is called on cleanup [1m[32mOK[39m[22m
    Arguments are not forwarded to the unsubscribe function [1m[32mOK[39m[22m

[1m  Exceptions thrown from the subscriber[22m
    Subscribe throws if the observer does not handle errors [1m[32mOK[39m[22m
    Subscribe does not throw if the observer has an error method [1m[32mOK[39m[22m
    Subscribe sends an error to the observer [1m[32mOK[39m[22m

[1m  Start method[22m
    If the observer has a start method, it is called [1m[32mOK[39m[22m
    Start is called with the observer as the this value [1m[32mOK[39m[22m
    Start is called with the subscription as the first argument [1m[32mOK[39m[22m
    Start is called before the subscriber function is called [1m[32mOK[39m[22m
    If unsubscribe is called from start, the subscriber is not called [1m[32mOK[39m[22m

[1mObservable.prototype[Symbol.observable][22m

[1m  Observable.prototype has a Symbol.observable method[22m
    Property Symbol(observable) exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 0 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Return value[22m
    Returns the 'this' value [1m[32mOK[39m[22m

[1mObservable.of[22m

[1m  Observable has an of property[22m
    Property of exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 0 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Uses the this value if it's a function[22m
    Observable.of will use the 'this' value if it is callable [1m[32mOK[39m[22m

[1m  Uses 'Observable' if the 'this' value is not a function[22m
    Observable.of will use 'Observable' if the this value is not callable [1m[32mOK[39m[22m

[1m  Arguments are delivered to next[22m
    All items are delivered and complete is called [1m[32mOK[39m[22m

[1mObservable.from[22m

[1m  Observable has a from property[22m
    Property from exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Allowed argument types[22m
    Null is not allowed [1m[32mOK[39m[22m
    Undefined is not allowed [1m[32mOK[39m[22m
    Undefined is not allowed [1m[32mOK[39m[22m

[1m  Uses the this value if it's a function[22m
    Observable.from will use the 'this' value if it is callable [1m[32mOK[39m[22m

[1m  Uses 'Observable' if the 'this' value is not a function[22m
    Observable.from will use 'Observable' if the this value is not callable [1m[32mOK[39m[22m

[1m  Symbol.observable method is accessed[22m
    Symbol.observable property is accessed once [1m[32mOK[39m[22m
    Symbol.observable must be a function [1m[32mOK[39m[22m
    Symbol.observable must be a function [1m[32mOK[39m[22m
    Symbol.observable must be a function [1m[32mOK[39m[22m
    Symbol.observable must be a function [1m[32mOK[39m[22m
    Calls the Symbol.observable method [1m[32mOK[39m[22m

[1m  Return value of Symbol.observable[22m
    Throws if the return value of Symbol.observable is not an object [1m[32mOK[39m[22m
    Throws if the return value of Symbol.observable is not an object [1m[32mOK[39m[22m
    Throws if the return value of Symbol.observable is not an object [1m[32mOK[39m[22m
    Throws if the return value of Symbol.observable is not an object [1m[32mOK[39m[22m
    Throws if the return value of Symbol.observable is not an object [1m[32mOK[39m[22m
    Returns the result of Symbol.observable if the object's constructor property is the target [1m[32mOK[39m[22m
    Calls the constructor if returned object does not have matching constructor property [1m[32mOK[39m[22m
    Constructor is called with a function [1m[32mOK[39m[22m
    Calling the function calls subscribe on the object and returns the result [1m[32mOK[39m[22m
    The subscriber argument is supplied to the subscribe method [1m[32mOK[39m[22m

[1m  Iterables: values are delivered to next[22m
    All items are delivered and complete is called [1m[32mOK[39m[22m

[1m  Non-convertibles throw[22m
    If argument is not observable or iterable, subscribe throws [1m[32mOK[39m[22m

[1mSubscriptionObserver.prototype.next[22m

[1m  SubscriptionObserver.prototype has an next method[22m
    Property next exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Input value[22m
    Input value is forwarded to the observer [1m[32mOK[39m[22m

[1m  Return value[22m
    Returns the value returned from the observer [1m[32mOK[39m[22m
    Returns undefined when closed [1m[32mOK[39m[22m

[1m  Method lookup[22m
    If property does not exist, then next returns undefined [1m[32mOK[39m[22m
    If property is undefined, then next returns undefined [1m[32mOK[39m[22m
    If property is null, then next returns undefined [1m[32mOK[39m[22m
    If property is not a function, then an error is thrown [1m[32mOK[39m[22m
    Method is not accessed until complete is called [1m[32mOK[39m[22m
    Method is not accessed when subscription is closed [1m[32mOK[39m[22m
    Property is only accessed once during a lookup [1m[32mOK[39m[22m

[1m  Cleanup functions[22m
    Cleanup function is called when next throws an error [1m[32mOK[39m[22m
    If both next and the cleanup function throw, then the error from the next method is thrown [1m[32mOK[39m[22m

[1mSubscriptionObserver.prototype.error[22m

[1m  SubscriptionObserver.prototype has an error method[22m
    Property error exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Input value[22m
    Input value is forwarded to the observer [1m[32mOK[39m[22m
    Additional arguments are not forwarded [1m[32mOK[39m[22m

[1m  Return value[22m
    Returns the value returned from the observer [1m[32mOK[39m[22m
    Throws the input when closed [1m[32mOK[39m[22m

[1m  Method lookup[22m
    If property does not exist, then error throws the input [1m[32mOK[39m[22m
    If property is undefined, then error throws the input [1m[32mOK[39m[22m
    If property is null, then error throws the input [1m[32mOK[39m[22m
    If property is not a function, then an error is thrown [1m[32mOK[39m[22m
    Method is not accessed until error is called [1m[32mOK[39m[22m
    Method is not accessed when subscription is closed [1m[32mOK[39m[22m
    Property is only accessed once during a lookup [1m[32mOK[39m[22m
    When method lookup occurs, subscription is closed [1m[32mOK[39m[22m

[1m  Cleanup functions[22m
    Cleanup function is called when observer does not have an error method [1m[32mOK[39m[22m
    Cleanup function is called when observer has an error method [1m[32mOK[39m[22m
    Cleanup function is called when method lookup throws [1m[32mOK[39m[22m
    Cleanup function is called when method throws [1m[32mOK[39m[22m
    If both error and the cleanup function throw, then the error from the error method is thrown [1m[32mOK[39m[22m

[1mSubscriptionObserver.prototype.complete[22m

[1m  SubscriptionObserver.prototype has a complete method[22m
    Property complete exists on the object [1m[32mOK[39m[22m
    Property has a function value [1m[32mOK[39m[22m
    Function length is 1 [1m[32mOK[39m[22m
    Property is writable [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Input value[22m
    Input value is forwarded to the observer [1m[32mOK[39m[22m
    Additional arguments are not forwarded [1m[32mOK[39m[22m

[1m  Return value[22m
    Returns the value returned from the observer [1m[32mOK[39m[22m
    Returns undefined when closed [1m[32mOK[39m[22m

[1m  Method lookup[22m
    If property does not exist, then complete returns undefined [1m[32mOK[39m[22m
    If property is undefined, then complete returns undefined [1m[32mOK[39m[22m
    If property is null, then complete returns undefined [1m[32mOK[39m[22m
    If property is not a function, then an error is thrown [1m[32mOK[39m[22m
    Method is not accessed until complete is called [1m[32mOK[39m[22m
    Method is not accessed when subscription is closed [1m[32mOK[39m[22m
    Property is only accessed once during a lookup [1m[32mOK[39m[22m
    When method lookup occurs, subscription is closed [1m[32mOK[39m[22m

[1m  Cleanup functions[22m
    Cleanup function is called when observer does not have a complete method [1m[32mOK[39m[22m
    Cleanup function is called when observer has a complete method [1m[32mOK[39m[22m
    Cleanup function is called when method lookup throws [1m[32mOK[39m[22m
    Cleanup function is called when method throws [1m[32mOK[39m[22m
    If both complete and the cleanup function throw, then the error from the complete method is thrown [1m[32mOK[39m[22m

[1mSubscriptionObserver.prototype.closed[22m

[1m  SubscriptionObserver.prototype has a closed getter[22m
    Property closed exists on the object [1m[32mOK[39m[22m
    Property has a getter [1m[32mOK[39m[22m
    Function length is 0 [1m[32mOK[39m[22m
    Property does not have a setter [1m[32mOK[39m[22m
    Property is non-enumerable [1m[32mOK[39m[22m
    Property is configurable [1m[32mOK[39m[22m

[1m  Returns false when the subscription is active[22m
    Returns false when the subscription is active [1m[32mOK[39m[22m

[1m  Returns true when the subscription is closed[22m
    Returns true after complete is called [1m[32mOK[39m[22m
    Returns true after error is called [1m[32mOK[39m[22m
    Returns true after unsubscribe is called [1m[32mOK[39m[22m

[90mPassed 196 tests and failed 0 tests, with 0 errors[39m

@rorticus
Copy link
Contributor Author

rorticus commented Dec 6, 2016

A couple of notes,

The ES spec tests have some pretty specific requirements about properties being non-enumerable, and certain object prototypes being Object, which is the cause for the weird factory methods I have. In addition, the properties must exist on the prototype of the object, not the object itself. As such, I need to create a new prototype each time I create a subscription.

I was wondering if you guys knew of an approach where I could use a static prototype, but still have certain internal variables (closed, cleanup, start, etc) not publicly accessible (i.e., in startSubscription I am capturing the executor and observer and using them later, but they shouldn't be accessible inside a subscribing object).

@codecov-io
Copy link

codecov-io commented Dec 6, 2016

Current coverage is 97.04% (diff: 100%)

Merging #62 into master will increase coverage by 0.40%

@@             master        #62   diff @@
==========================================
  Files            29         30     +1   
  Lines          1129       1285   +156   
  Methods          23         24     +1   
  Messages          0          0          
  Branches        211        248    +37   
==========================================
+ Hits           1091       1247   +156   
  Misses           21         21          
  Partials         17         17          

Powered by Codecov. Last update a4089d9...a33e47b

Copy link
Member

@agubler agubler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lost of else, else if and catch's that need to be on a new line.

*/
function startSubscription<T>(executor: Subscriber<T>, observer: Observer<T>): Subscription {
let closed = false;
let cleanUp: any;
Copy link
Member

@agubler agubler Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can type cleanUp to the union type return of the executor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So cleanUp is either undefined or a method with no parameters and no return value, so I've typed it as such (not just the return value of executor).

else if (result && 'unsubscribe' in result) {
cleanUp = result.unsubscribe;
}
else if (result !== undefined && result !== null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it just be (result)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not.. if the return value is undefined or null, no error. But if the return value is the number 0 or false, we want to error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roger that.

} else {
throw new TypeError('Observer.complete is not a function');
}
} else if (cleanUpError) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line for else if

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And else above ^^^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed all those :)

Copy link
Member

@agubler agubler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice one @rorticus

@rorticus rorticus merged commit 8617e48 into dojo:master Dec 7, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants