1

I have some Coffeescript that looks like this (apologies for the complexity in advance):

doc = new ChargerServerDoc(Chargers.find({id:site.id}), site)

doc.set_defaults().merge().needs_update
  update: (id, doc) ->
    Chargers.update id, $set: doc, (error, result) ->
      if error
        run_stats.error_count += 1
        "error"
      else
        run_stats.update_count += 1
        "update"

    return

  insert: (doc) ->
    Chargers.insert doc, (error, result) ->
      if error
        run_stats.error_count += 1
        "error"
      else
        run_stats.insert_count += 1
        "insert"

    return

It's supposed to create some sort of document and implement insert or update to the database as callbacks.

needs_update: (callbacks = null) ->

  console.log inspect arguments

  if callbacks is null
    return true unless @is_equal(@working_document, @retrieved_document)
    return false
  else
    console.log """
    callbacks not null:
    insert: #{inspect callbacks['insert']}
    update: #{inspect callbacks['update']} 
    """
    data = @get()
    if @is_equal(@working_document, @retrieved_document)
      throw 'requires update callback' if _.isEmpty(callbacks.update)
      return callbacks.update.call(this, data._id, _.omit(data, '_id'))
    else
      throw 'require insert callback' if _.isEmpty(callbacks.insert)
      return callbacks.insert.call(this, _.omit(data, '_id'))

As you can see, the needs_update function is peppered with console.log statements. This is running in node.js, and it's a run-once-at-startup thing. So it's not easy to watch in the node inspector. At least I haven't figured out how.

In any case, the interesting part of this is the console.log inspect arguments. inspect just converts the object to a JSON string so I can read it. And the result is always {"0":{}}.

And that's where I'm stuck. I can see it's passing a hash, but there's nothing in the hash. Stranger still, this same behavior occurs when I hand write it in pure Javascript.

I tried to reduce this and reproduce it to no avail. This code works:

h =
  f1: (a) -> 'one'
  f2: (b) -> 'two'

test = (fn) ->
  console.log fn.f1.call()
  console.log fn.f2.call()

test(h)

Does anyone see why the first code fails and the reduced example works?

Thanks!

3
  • Yes. It calls JSON.stringify() (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…). Commented Jan 3, 2015 at 20:56
  • @muistooshort-- the console.log that starts with callbacks not null does very much what you say: It examines the two key/value pairs I expect and they both come up undefined. I've tried to simplify it, but at the core, I don't understand why the arguments are turning up with an empty hash. Commented Jan 3, 2015 at 22:10
  • It turns out the code is fine. The thing that was going wrong was that throw 'requires update callback' if _.isEmpty(callbacks.update) and the same for insert were firing even though the callbacks were being provided. For some reason, the provided functions evaluated as empty by Underscore. Removing the lines that threw the exceptions solved the problem. So I'll reframe the question: Why would these tests for absence of a callback evaluate to true? Commented Jan 4, 2015 at 19:21

1 Answer 1

2

You're not use _.isEmpty correctly. From the fine manual:

isEmpty _.isEmpty(object)

Returns true if an enumerable object contains no values (no enumerable own-properties). For strings and array-like objects _.isEmpty checks if the length property is 0.

A function is not an enumerable object, nor is it a string or array-like object. You're giving _.isEmpty something that it doesn't understand so you're invoking unspecified behavior. Turns out that _.isEmpty returns true for anything it doesn't understand.

If you want to see if something is a function, _.isFunction would probably serve you better:

throw 'requires update callback' unless _.isFunction(callbacks.update)
Sign up to request clarification or add additional context in comments.

3 Comments

I was checking whether the argument was supplied rather than whether it was a function. You're probably right that checking that it's a function makes sense but I'm not sure isEmpty is really being used wrong. Looking at the code on Github, the comments and the code tell me that if you pass null, you get true. Here: if (obj == null) return true;. (github.com/jashkenas/underscore/blob/master/underscore.js#L1168) So while the comment does not explicitly reference null as a possible "empty" value, the code does. I believe this is how almost all empty or blank functions work.
Accepted as the correct answer because the test really should be for whether a callback function is supplied.
Yes, the code considers null (and undefined, hence the == null instead of === null) as "empty" but it is still expecting certain types of non-null inputs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.