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 |