Question

I'm trying to bring some sanity to a legacy Classic ASP application, and as part of this I'm trying to write a Fluent API for some JScript classes that I have created.

e.g. myClass().doSomething().doSomethingElse()

The concept is outlined here (in VBScript)

This is my sample JScript class:

var myClass = function () {
    this.value = '';
}

myClass.prototype = function () {

    var doSomething = function (a) {
        this.value += a;
        return this;
    },

    doSomethingElse = function (a, b) {
        this.value += (a + b);
        return this;
    },

    print = function () {
        Response.Write('Result is: ' + this.value + "<br/>");
    }

    return {
        doSomething: doSomething,
        doSomethingElse: doSomethingElse,
        print: print
    };

}();

/// Wrapper for VBScript consumption
function MyClass() {
    return new myClass();
}

In the existing VBScript code I'm then trying to chain the methods together:

dim o : set o = MyClass()
'' This works
o.doSomething("a")

'' This doesn't work
o.doSomething("b").doSomethingElse("c", "d")

'' This for some reason works
o.doSomething("e").doSomethingElse("f", "g").print()

When the functions have more than one parameter I get the "Cannot use parentheses when calling a Sub" VBScript error. Strangely, it seems to work when followed by another method.

I understand that parentheses should be ommitted when calling a sub. However:

1. Why is it being recognised as a Sub if there is a return value?

2. Is there any way around this in order to implement my Fluent API?

Was it helpful?

Solution 2

You can put call at the start of each line of vbscript. This will accept brackets being put on each method call, e.g.

' This works
call o.doSomething("b").doSomethingElse("c", "d")

' This works too
call o.doSomething("e").doSomethingElse("f", "g").print()

Or just don't put the parantheses in the vbscript when it complains...

See Differences between VBScript and JavaScript

OTHER TIPS

The rule is: No param list () when calling a 'named piece of code' as a sub. The nasty trap is: For single parameter npcs, the () may look like param list (), but are (interpreted as) pass-me-by-value ().

Your set o = MyClass() calls my MyClass() as a function; that is clear from the assignment.

Your o.doSomething("a") calls .doSomething as a sub, the () are seen as pass-me-per-value (); doSomething "a" would be the correct call.

The first part of your o.doSomething("b").doSomethingElse("c", "d") works, because o.doSomething("b"). is/uses a function call to get an object whose .doSomethingElse() is to be called; the second part .doSomethingElse("c", "d") can't work, because it isn't a sub call (no return value used/assigned) and the () can't be seen as pass-me-by-value (). o.doSomething("b").doSomethingElse "c", "d" would be correct.

The first part o.doSomething("e").doSomethingElse("f", "g").print() (upto the .print) is a function call (to get the object that will .print), so the () are param list (); the () after .print are wrong, but the compiler/interpreter let them slip by.

To sum up: don't use () when you don't want something back.

WRT comment:

To put it the other way around: use () when you want something back!

set o1 = MyClass() <- return value should go into o1

set o2 = o.S(1, 2).S(3, 4).S(5, 6) <- first two return values are needed/used for the next call; last return value goes into o2.

o.S(1, 2).S(3, 4).S 5, 6 <- last return value is discarded - no sub call

The () rule is about what you do (use the return value or not), not about what the npc is.

Incidentally you can write a fluent API in VBScript. I built a library to facilitate inline creation of HTML to avoid the "tag soup" garbage and I'm using it in an app right now (that's how I found this question).

Here's an example with a couple helpers wrapping the 960gs grid:

with html
  .open
    with head
      .open
        stylesheet_link "assets/stylesheets/960gs/reset.css"
        stylesheet_link "assets/stylesheets/960gs/960.css"
        stylesheet_link "assets/stylesheets/960gs/text.css"
        stylesheet_link "assets/stylesheets/app.css"
      .close
    end with
    with body
      .open
        with grid_container(12)
          .open
            call main
          .close
        end with
      .close
    end with
  .close
end with

And then main could be similar to this:

with grid_row
  with grid_column(4)
    .id "nav-left"
    .open
      with ul
        .open
          li.open.contains( link_to("Google", "http://google.com") ).close
          li.open.contains( link_to_if(a > b, "Microsoft", "http://microsoft.com") ).close
          li.open.contains( link_to_unless(a > b, "Apple", "http://apple.com") ).close
        .close
      end with
    .close
  end with
  with grid_column("8 push_2")
    .id "main-content"
    .open
      h1.open.contains("Title Here").close
      do until recordset.eof
        p.class("record").open.contains(recordset("column_name")).close
        recordset.movenext
      loop
    .close
  end with
end with

The way the lib is written it could generate XML with essentially zero effort, basically any tag language that has the "nested elements with attributes" structure.

Classic ASP looks the way it does because the people who use(d) it and the people who made the tutorials just threw it out there and didn't fully understand the language and good practices. So we are stuck maintaining meandering code vomit. But if you do it right it can start to look like ruby. Heck I even implemented some ruby iteration stuff using dynamic vbscript classes a few years ago just to see if I could do it and it worked, but I lost that code a long time ago. Point being that even though vbscript is limited it still has more capability than people give it credit for, they just usually don't do things as well as could be done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top