Don’t use array as argument for AJAX call with Prototype

I’m using Prototype JavaScript library and today I have learned one thing. To make Your life easier do not use JavaScript Arrays as arguments for AJAX calls.

I’m talking about parameters option in Ajax objects (Ajax.Request, Ajax.Updater, etc). Let’s take simple HTML snippet (public/test.html):

<div id="out"></div>
<script src="/javascripts/prototype.js" type="text/javascript"></script>
<script type="text/javascript">
    addr = []
    addr[1] = 2
    new Ajax.Updater ("out", "/controllers/test", {parameters: addr })
</script>

Now simple Controller controller :)

class ControllersController < ApplicationController
  def test
    render :layout => false
  end
end

And test.html.erb:

<%= debug params %>

Simple, isn’t it? What do You expect to be displayed when You point Your browser to /test? Something like:

--- !map:HashWithIndifferentAccess 
action: test
"1": "2"
controller: controllers

Wrong!

Real output is like:

--- !map:HashWithIndifferentAccess 
uniq: |-
  function (sorted) {
      return this.inject([], function (array, value, index) {if (0 == index || (sorted ? array.last() != value : !array.include(value))) {array.push(value);}return array;});
  }
without: |-
  function () {
      var values = $A(arguments);
      return this.select(function (value) {return !values.include(value);});
  }
size: |-
  function () {
      return this.length;
  }
zip: |-
  function () {
      var iterator = Prototype.K, args = $A(arguments);
      if (Object.isFunction(args.last())) {
          iterator = args.pop();
      }
      var collections = [this].concat(args).map($A);
      return this.map(function (value, index) {return iterator(collections.pluck(index));});
  }
toArray: |-
  function () {
      return [].concat(this);
  }
findAll: |-
  function (iterator, context) {
      iterator = iterator.bind(context);
      var results = [];
      this.each(function (value, index) {if (iterator(value, index)) {results.push(value);}});
      return results;
  }

[CUT]

What is going on?

It seems that it is some bug with serialization of Arrays. Prototype does iterate through all properties, and dumps them not matter if it property is function or not (maybe Prototype has own reasons for that behavior). So how to get output like in the first dump? Use Hash, Luke:

//    addr = []
    addr = {}

Does work as expected.

How to write DSL (with Acts As Tree in background)

Acts As Tree is useful plugin providing abstract layer for Your models to form tree structures. I was using it recently to store some document structures in DB. Plugin works as a charm and there is not too much to write about. Just follow README ;)

tree.jpg

But I needed to create documents somehow, and provide easy way to upload them to DB during migrations. SQL is not a solution as is fragile to schema changes. Write definitions in pure Ruby results in long, unreadable and error prone code.

So what to do? Write DSL to handle this task. By the way – writing DSLs is one of killer features of Ruby, IMO.

Why not use something like hpricot and parse HTML source and create tree? Well I think that in this case – limited subset of tags to implement and some additional logic which is not always easy extractable from HTML that approach was simpler. Output from some nodes is complex, not single tag, so parsing it from HTML source is more complicated.

Or to say it other way – I started with DSL, it took 1 hr to write it and DSL helped to get job done, so… It was good enough for me.

So here it comes – example how You can create DSL for own application. This was code for prototype application, so for sure it can be done in some more polished way. I wait for some comments, what You would change.

But first word about tree nodes. Since I needed to store different types of elements I decided to store in tree nodes class name of final classes and arguments to for initialize call. That way tree structure is independent from nodes types. As a contra there is overhead for creation of final object (first we need to fetch and create AR object for tree node and then create final object). But in this case result from tree generation is subject to caching (since it is not changing) so it is OK. I will write more about how I was using this structure in separate post.

Where to start with DSL?

Continue reading

NetManiac – reloaded

This year have stared for me with permanent lack of time. In January I was finishing Ruby project, which was started over one year ago. And last six weeks I was involved in creation of prototype application (Rails of course) for some Germany startup. It seems that this stage is over and I did not choose to participate in next phase. Why? There is plenty of reasons, but most important – next few weeks/months will be filled with work on my own projects. One of this projects is to bring back some life to this blog, so expect me to write much often here.

I will keep topics around Ruby and web development. That means Rails, but probably also Merb, in which I become recently interested in, JavaScript and APIs like GoogleMaps, Facebook API.

Speaking of last – I will deliver talk about Facebook API on RuPy 2008. I do prefer show practical aspects of using API and I will try to do some live coding (again, similar to last year).

RuPy is conference devoted to Ruby and Python languages. Talks are divided into two tracks (Ruby and Python, have You seen this coming? :) ) and from my last year experience it is great place to get know new people. This time I hope I will be able to attend all program including evening integration :) which I missed last year.


PS
I was struck by thought that writing this post on Easter in first days of spring definitely is some sign of times. And speaking of spring – snow outside is getting heavier ;)