PullMonkey Blog


30 Apr

Open Flash Chart II - fully automated


Just as an attention grabber - we are going after this example in this article:

Keeping up

Ok, seeing that the php versions of open flash chart and open flash chart swf files continually change along with with the API (not saying this is a bad thing), I wanted to come up with an even more abstract solution. The goal is to not have to worry when the swf file is released with the latest set of graphs or changes its API. I simply don't want to worry about this method or that method, or this class or that class.

Feedback

This article will sort of act as a tutorial for those interested in metaprogramming and as a set of instructions for those looking to experiment with the latest version of the OFC II Rails Plugin that I am currently toying with. I would like to hear feedback, but just remember that phase 1 of this release will be very basic, meaning none of the ajaxy stuff. It will come, just not yet.

Let's see what we can get away with

I am already using method_missing() for pretty much everything in the OFC II Rails Plugin that is being used now. But every time new classes are added, I have to sit down and basically convert the php class to ruby - just plain tedious, not really what I had planned when I started all this. Ok, so method_missing() was great, but let me introduce (or possibly reintroduce) you to const_missing(), basically method_missing() but instead of methods, we can create classes or modules or other objects on the fly. This will definitely help when the php version gets a new class. Instead of getting hounded to update the rails version to be 100% like the php version, everything will just work, no updates to code required. Well, we hope ! So check this out:



Here is what we did with method_missing():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

module OFC
  class Base
    def method_missing(method_name, *args, &blk)
      case method_name.to_s
      when /(.*)=/   # i.e., if it is something x_legend=
        # if the user wants to set an instance variable then let them
        # the other args (args[0]) are ignored since it is a set method
        self.instance_variable_set("@#{$1}", args[0])
      when /^set_(.*)/
        # backwards compatible ... the user can still use the same set_y_legend methods if they want
        self.instance_variable_set("@#{$1}", args[0])
      else
          if inst = self.instance_variable_get("@#{method_name}")
            inst
          else
            # if the method/attribute is missing and it is not a set method then hmmmm better let the user know
            super
          end
      end
    end
  end
end

This just basically allows me to do this:

1
2
3
4
5
6
7
8
9
10
11
12
13

  class Foo < OFC::Base
  end

  foo = Foo.new

  foo.some_random_attribute = "Hello"  #=> "Hello"
  foo.some_random_attribute  #=> "Hello"
  foo.some_random_undefined_attribute  #=> Method Missing error (calls super)

  # too be like php, for easier conversion
  foo.set_some_random_attribute("Good Bye")  #=> "Good Bye"
  foo.some_random_attribute  #=> "Good Bye"

Along the same lines, I have created an initialize method that takes any argument hash of variable/value pairs and calls variable=() which is handled by method missing as we saw above:

1
2
3
4
5
6
7
8

  class Foo < OFC::Base
  end

  foo = Foo.new(:x_axis => 5, :min => 10, :max => 90, :steps => 5, :elements => ["one", "two"])
  
  foo.x_axis #=> 5
  foo.min #=> 10

Ok, so on to const_missing() and what we can do with that:

1
2
3
4
5
6

  def OFC.const_missing(const)
    klass = Class.new OFC::Base
    Object.const_set const, klass
    return klass
  end

This says that any undefined (missing) constant of OFC should be defined as a new class that inherits from OFC::Base.

So when we say OFC::Foo, that has not been defined, so we will get back class OFC::Foo < OFC::Base;end; which will give us the initialize() method and method_missing() method from above. Let's see how this works:

1
2
3
4
5
6
7
8
9
10
11

  line = OFC::Line.new(:values => [1,2,3,nil,nil,5,6,7])
  line.values #=> [1,2,3,nil,nil,5,6,7]
  line.some_random_variable = "Hello" #=> "Hello"
  line.some_random_variable #=> "Hello"

  stacked_bar_chart = OFC::BarStack.new
  stacked_bar_chart.values = []
  stacked_bar_chart.values << [2,3,4]
  stacked_bar_chart.values << [5, {"val" => 5, "colour" => "#ff0000"}]
  stacked_bar_chart.keys = [{ "colour" => "#C4D318", "text" => "Kiting", "font-size" => 13 } ...]

So it all sort of came together right there. I've shown you all the code that comes with the Rails Open Flash Chart plugin now. No more definining idividual classes, no more trying to keep up with the never ending php version, and no more late nights converting php to ruby (!). About dang time.



Ok, but this is just the beginning, nothing has been set in stone, so like I said, give me your feedback, what works for you and what does not. And, hopefully, I will have solutions for you or you for me.


Example with new version (test version)

I am using rails 2.3.2, but I don't think it will matter what version you are using.

Create your new rails project

1
2
3
4
5

# create a new rails project 
> pullmonkey$ rails testing_it
#<Bunch of stuff is created ....>
> pullmonkey$ cd testing_it/

Install the plugin from the test branch

Note the -r test in this next step. The new version (test version) I am playing with is under the test branch and -r says what branch to pull from.

Also, you can use git:// instead of http:// below, but depending on your firewall restrictions http:// will probably work out best for you.

1
2
3

> pullmonkey$ ./script/plugin install http://github.com/pullmonkey/open_flash_chart.git -r test
# <Bunch more stuff ...>

Create a controller to play in

1
2
3

> pullmonkey$ ./script/generate controller test_it
# <And more stuff >

Get our assets

1
2
3
4
5
6
7

# first we will get swfobject.js
> pullmonkey$ cp vendor/plugins/open_flash_chart/assets/javascripts/swfobject.js public/javascripts/
# next the open flash chart swf (GET whatever is the latest version), right now that is here: http://teethgrinder.co.uk/open-flash-chart-2/open-flash-chart.swf
> pullmonkey$ cd public/
> pullmonkey$ wget http://teethgrinder.co.uk/open-flash-chart-2/open-flash-chart.swf
> pullmonkey$ cd ..

Edit our controller

Notice here that I just include one of the many examples from the plugin's examples directory. Definitely more to follow.

One thing you will notice about the examples, is that the php code is in the comments, so you can see how I would convert from the php examples to ruby. Please feel free to add your own examples, just fork the project.

1
2
3
4
5
6
7
8
9
10

> pullmonkey$ vi app/controllers/test_it_controller.rb
# mine looks like this:
class TestItController < ApplicationController
  include OFC::Examples::AreaHollow

  def index
    @graph = open_flash_chart_object(600,300, "/test_it/area_hollow")
  end
end

Edit our view

1
2
3
4
5

> pullmonkey$ vi app/views/test_it/index.html.erb
# mine looks like this:
<%= javascript_include_tag 'swfobject' %>
<%= @graph %>

Start 'er up

1
2
3
4
5

> pullmonkey$ ./script/server

# browse to the test_it index
http://localhost:3000/test_it

Our example


30 Apr

Spreadsheet Gem - data may have been lost


I've been using the spreadsheet gem lately for a couple projects I am working on to modify existing spreadsheets. I have quite often stumbled upon this error when opening modified spreadsheets in excel:


File error: data may have been lost

Like most microsoft errors, it was useless and the spreadsheet came up just fine. But that error was just so annoying, other spreadsheet applications (open office, excel on mac) opened without any problems. So after quite a bit of hacking and digging around, I finally tried setting the encoding, which defaults to UTF-8. Well it just so happens that the spreadsheet being modified was encoded with UTF-16LE.



So part one of my solution became this:

1
2

Spreadsheet.client_encoding = 'UTF-16LE'

Then doing a little more digging I decided that this would be a better long-term solution:

1
2
3

book = Spreadsheet.open spreadsheet_file
Spreadsheet.client_encoding = book.encoding

Well, hopefully it wasn't just me and someone will be able to save a bit of time with this.



17 Apr

Can you read this?


Got an email today, I have seen it before and I am sure it has been going around for years. This time, I thought that I would do an exercise and create a plugin that duplicates what I found in this email. See for yourself.



Here is the email I got:






What I gathered was that the only important letters are the first and last letter of each word, those have to be in the right order. So the rest of the letters can be in any random order. That is what I did - I created a plugin and put it out on github. You can install it like this:

./script/plugin install http://github.com/pullmonkey/can_you_read_this.git

And use it like this:

#in your views <%= can_you_read_this("hello, can you read this?") %>

Have fun.


14 Apr

ECE231 - Spring 2009 - Programming Assignment 4


This is a step-by-step tutorial for this assignment and an explanation of the basics of how it works for those that are having a difficult time understanding or just are stuck somewhere. To view the assignment click here.

So for this assignment we are only going to be doing input and output. So the only header file we need is the iostream library.

1
2
3
4

#include <iostream>
using namespace std;

Defining the Node Class

For the node class we need 5 private variables: type, value, node1, node2, next, a constructor, 5 member functions, and a friend class.

Because most of the functions only return a single variable it would be a waste to have the functions outside the class so I put them all inside the class.

So here is what your class should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

class node
{
  protected:
    int type;     // type of component
    double value; // value of the component
    int node1;    // first node of the component
    int node2;    // second node of the component
    node *next;   // pointer to the next node in list
  public:
    node(int a, double b, int c, int d)
    {
      type  = a;
      value = b;
      node1 = c;
      node2 = d;
    }

    int getType() { return type; }
    double getValue() { return value; }
    int getNode1() { return node1; }
    int getNode2() { return node2; }
    node *getNext() { return next; }

    friend class circuit;
};

Defining the Circuit Class

For the circuit class we need 2 private variables: first and last, a constructor, a copy constructor, 6 member functions, and a friend function.

For these I will put most of the functions on the outside of the class.

So here is what your class should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class circuit
{
  protected:
    node *first; // pointer to first node in list
    node *last;  // pointer to last node in list
    node *getNode(int & , double & , int & , int & );
  public:
    circuit();
    circuit(const circuit & );
    ~circuit();

    void insert(int , double , int , int );
    void insertInFront(node * );
    void insertInBack(node * , node * );
    void insertInMiddle(node * , node * , node * );
    bool remove(int , int );
    bool isEmpty();

    friend ostream & operator << (ostream & , circuit );
};

Writing the Actual Functions for the Circuit Class

First, we have the private function getNode. This function creates a new member of the node class and returns the address to it.

1
2
3
4
5
6
7
8

node* circuit::getNode(int &t, double &v, int &n1, int &n2)
{
  node *temp = new node(t, v, n1, n2);
  assert( temp != 0 );
  return temp;
}

Second, we have the a constructor, a copy constructor, and a class destructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

circuit::circuit()
{
  first = last = 0;
}
circuit::circuit(const circuit &c)
{
  first = last = 0;
  node *nc = c.first;
  while(nc != 0)
  {
    insert(nc->type, nc->value, nc->node1, nc->node2);
    nc = nc->next;
  }
}
circuit::~circuit()
{
  if( !isEmpty() )
  {
    node *nc = first;
    node *temp;
    while(nc != 0)
    {
      temp = nc;
      nc = nc->next;
      delete temp;
    }
  }
}

Next, we have the 6 member functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

void circuit::insert(int t, double v, int n1, int n2)
{
  node *newNode = getNode(t, v, n1, n2);
  node *nc = first;
  node *nc_next = first;
  if(isEmpty())
  {
    first = last = newNode;
    newNode->next = 0;
  }
  else if(first == last)
  {
    if(newNode->node1 < first->node1)
      insertInFront(newNode);
    else if(newNode->node1 > first->node1)
      insertInBack(newNode, first);
    else // if newNode->node1 == first->node1
    {
      if(newNode->node2 < first->node2)
        insertInFront(newNode);
      else
        insertInBack(newNode, first);
    }
  }
  else
  {
    while(nc->next != 0)
    {
      if(newNode->node1 < nc_next->node1)
      {
        if(nc_next == first)
          insertInFront(newNode);
        else
          insertInMiddle(newNode, nc, nc_next);
        break;
      }
      if(newNode->node1 == nc_next->node1)
      {
        if(newNode->node2 <= nc_next->node2)
        {
          if(nc_next == first)
            insertInFront(newNode);
          else
            insertInMiddle(newNode, nc, nc_next);
          break;
        }
      }
      if(newNode->node1 >= nc_next->node1 and nc_next == last)
      {
        insertInBack(newNode, nc_next);
        break;
      }
      nc = nc_next;
      nc_next = nc->next;
    }
  }
}
void circuit::insertInFront(node *n)
{
  n->next = first;
  first = n;
}
void circuit::insertInBack(node *n, node *p)
{
  p->next = last = n;
  n->next = 0;
}
void circuit::insertInMiddle(node *cn, node *p , node *n)
{
  p->next = cn;
  cn->next = n;
}
bool circuit::remove(int n1, int n2)
{
  node *nc = first;
  node *nc_next = first;
  node *temp;
  if( isEmpty() )
    return false;
  while (nc_next != 0)
  {
    if(nc_next != 0 and nc_next->node1 == n1 and nc_next->node2 == n2)
    {
      temp = nc_next;
      nc->next = nc_next->next;
      if(first == last)
        first = last = 0;
      if(nc_next == first)
        first = temp->next;
      if (nc_next == last)
        last = nc;
      delete nc_next;
      return true;
    }
    nc = nc_next;
    nc_next = nc->next;
  }
  return false;
}
bool circuit::isEmpty()
{
  return ( first == 0 and last == 0 );
}

Last, we have a friend function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

ostream & operator << (ostream &os, circuit c)
{
  node *nc = c.first; // node count variable used for looping through list
  int lcv = 1; // loop control variable
  cout << "First: " << c.first << "  Last: " << c.last << endl;
  while (nc != 0)
  {
    os << lcv << ". "
       << "node1: "   << nc->getNode1() 
       << "  node2: " << nc->getNode2()
       << "  type: "  << nc->getType() 
       << "  value: " << nc->getValue() 
       << "  next: "  << nc->getNext() 
       << endl;
    nc = nc->getNext();
    lcv++;
  }
  if(c.first == 0)
    os << "List is empty" << endl;
  return os;
}

Testing your program

Our teacher gave us a test program, however I expanded it a little bit to test a bit more fully.

So here is my test program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

#include <iostream>
#include "yourprogramname.h"
using namespace std;

const int M=7;
const int R=2;

int main()
{
  circuit c, c2, c3;
  int d[M*3] = {1,1,4, 1,1,3, 2,2,3, 1,1,2, 1,5,6, 1,2,5, 3,1,3};
  double v[M] = {10.5, 30.7, 100.4, 20.6, 35.5, 45.9, 60.8};
  int d1[R*8] = {1,2, 5,6, 3,6, 1,4, 1,3, 2,5, 2,3, 1,3};
  int i, j;

  for( i=0,j=0; i<M; i++,j=j+3 )
  {
    cout << i+1 << endl;
    cout << "Inserting new node into list with:" << endl;
    cout << "node1: "   << d[j+1]
         << "  node2: " << d[j+2]
         << "  type: "  << d[j]
         << "  value: " << v[i]
         << endl;
    c.insert( d[j], v[i], d[j+1], d[j+2] );
    cout << "List: " << endl;
    cout << c << endl << endl;
  }
  for( i=0; i<R*8; i=i+2 )
  {
    cout << "Removing node with:" << endl;
    cout << "node1: "   << d1[i]
         << "  node2: " << d1[i+1]
         << endl;
    if( c.remove( d1[i], d1[i+1] ) == false )
      cout << "Node not found" << endl;
    cout << "List: " << endl;
    cout << c << endl << endl;
  }

  cout << "nnMaking List #2:" << endl;
  int d2[M*3] = {2,2,4, 1,1,3, 2,2,3, 1,1,2, 1,5,6, 1,2,5, 3,1,3};
  for( i=0,j=0; i<M; i++,j=j+3 )
  {
    cout << i+1 << endl;
    cout << "Inserting new node into list with:" << endl;
    cout << "node1: "   << d2[j+1]
         << "  node2: " << d2[j+2]
         << "  type: "  << d2[j]
         << "  value: " << v[i]
         << endl;
    c2.insert( d2[j], v[i], d2[j+1], d2[j+2] );
    cout << "List: " << endl;
    cout << c2 << endl << endl;
  }

  cout << "nnMaking List #3:" << endl;
  int d3[M*3] = {2,2,4, 3,3,3, 2,2,3, 1,1,2, 1,5,6, 1,2,5, 3,1,3};
  for( i=0,j=0; i<M; i++,j=j+3 )
  {
    cout << i+1 << endl;
    cout << "Inserting new node into list with:" << endl;
    cout << "node1: "   << d3[j+1]
         << "  node2: " << d3[j+2]
         << "  type: "  << d3[j]
         << "  value: " << v[i]
         << endl;
    c3.insert( d3[j], v[i], d3[j+1], d3[j+2] );
    cout << "List: " << endl;
    cout << c3 << endl << endl;
  }

  return 0;
}