Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I create the following chainable functions? #559

Closed
holgersindbaek opened this issue Sep 27, 2016 · 9 comments
Closed

How do I create the following chainable functions? #559

holgersindbaek opened this issue Sep 27, 2016 · 9 comments

Comments

@holgersindbaek
Copy link

I would like to be able to have a more Ruby like syntax, so being able to do the following on the "Object" would be great:

Object("one").isEqual("two") // false
Object("one").isNotEqual("two") // true
Object(false).isTrue() // false
Object(false).isFalse() // true

Is it possible to create these chainable functions? How would I do that in Sugar?

@andrewplummer
Copy link
Owner

Yes! That is now possible through the use of chainables.

@holgersindbaek
Copy link
Author

@andrewplummer That sounds great. I've just spend the better part of an hour trying to figure out the correct Syntax for creating such chainable functions. Can you give an example?

Right now my code looks like this:

Sugar.Object.defineStaticWithArguments
  isNotEqual: (objectOne, objectTwo) ->
    !Sugar.Object.isEqual(objectOne, objectTwo)

Sugar.Object.defineStatic
  isPresent: (object) ->
    !Sugar.Object.isEmpty(object)

  isTrue: (object) ->
    Sugar.Object.isEqual(object, true)

  isFalse: (object) ->
    Sugar.Object.isEqual(object, false)

Sugar.Object.defineInstance
  isTrueTest: (object) ->
    Sugar.Object.isEqual(object, true)

Sugar.extend()

But when I try to get if the object is true or false, then I get this error:

Object(undefined).isFalse() // Uncaught TypeError: Object(...).isFalse is not a function(…)

I know I can do this:

Sugar.Object(undefined).isTrueTest() // SugarChainable {raw: false}

But writing "Sugar.Object" seems a bit long and I can't really use it for much if I don't get back the true/false result.

@andrewplummer
Copy link
Owner

You seem to have it set up correctly. When you run extend it will not extend Object.prototype by default. You can force it to do this by running:

Sugar.extend({
  objectPrototype: true
});

However this is highly discouraged as it can have lots of unintended consequences. Also, even if you do this your example still will not work as undefined is not an actual object with a prototype and so cannot have it's methods mapped.

The idea behind chainables is that they are always used through the Sugar global, so Object(undefined) isn't running through Sugar in any way (you may already know this). Calling extend will only map methods onto natives and their prototypes, nothing else.

If using Sugar.Object (or ideally new Sugar.Object) is too long you can map it to something shorter like $O, etc.

As for the result, this is intended behavior, so the result can be accessed through the .raw property. The property is intentionally short so it shouldn't add much overhead to your code. Additionally valueOf is mapped to this property so although you can't do truthy/falsy tests, if you use ==, it will unwrap and you won't have to use .raw.

@holgersindbaek
Copy link
Author

Thanks for the thorough explanation. I have 2 last questions:

  1. Can I map to "Sugar" to "S", so my function would look like this:

    S.Object("foo").isEqual("bar")

  2. Is there any way I can get back a true/false property, so I can do something like this:

    Sugar.Object("Test").isEqual("sdsd") ? "true" : "false" // "false"

@andrewplummer
Copy link
Owner

  1. Yes! You can map any variable to another, right? So mapping the global (as you have) or one of it's "namespaces" (as I did above with $O) is totally valid.
  2. Unfortunately, no. Chainables always return objects (this is what allows them to stay "chainable"), and objects are always truthy. What you're asking, however is similar to what another user proposed and lead me to this proposal. It seems to make common sense when coming from a perspective like yours that isXXX methods should NOT return chainable objects, but instead booleans, however it does mean that the statement chainable methods always return a new chainable can no longer be true, which was the starting point for this feature. The fact that you can't use standard JS control flow without unwrapping the chainables seems like a hurdle here, however, so I'm leaning toward this proposal for the next version, however it will need to be documented well, and I'll have to look over method names to make sure that it's crystal clear how this works.

@andrewplummer
Copy link
Owner

To answer your question, though, for now you will need to do either:

Sugar.Object("Test").isEqual("sdsd").raw ? "true" : "false" // "false"

or

Sugar.Object("Test").isEqual("sdsd") == true ? "true" : "false" // "false"

These are 2 different ways to "unwrap" the resulting chainable.

@holgersindbaek
Copy link
Author

Hmm ok. I'll be looking forward to the next version as well. If chainable functions could return true/false values, it would add a lot to the readability of the code IMO.

Thanks for the clarification.

@andrewplummer
Copy link
Owner

Agreed. Can you also give the proposal a thumbs up? I'm trying to use that to gauge which proposals to focus on for the next version.

@holgersindbaek
Copy link
Author

Sure thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants