read more | digg story
read more | digg story
Currently the AATT is a real plugin that lives in /vendor/plugins in your rails app. The plugin enables you to do that to any of your models
class Person < ActiveRecord::Base
acts_as_taggable_tag
end
This simple invocation adds the following to your model class
#These methods are called to define the relations
has_many :tag_joins, :class_name => "Tagging", :as => :tagged_one
has_many :tagged_one_joins, :class_name => "Tagging", :as => :tag
#These instance methods are defined for your model
tags #returns a list of objects that tag yours
tagged_ones #returns a list of objects that are tagged by you
tag(tagged_one) #tag this object by yourself
remove_tag(tag) #remove this tag from you
clear_all_tags #delete the relations between you and your tags
clear_all_tagged_ones #delete the relations between you and objects tagged by you
If you look at the implementation of the above methods you'll notice how inefficient they are (a select call for each tag on a certain object for example). Performance is not my primary concern at this point in time, I am just trying to get the concept right.
A class is created for the dual polymorphic join model (name Tagging). Currently the name and the table mappings are not configurable (you have to use what I give you, period). The table structure is available in a migration format and can be invoked by:
rake import_aatt_schema
and it can be dropped from the database using:
rake drop_aatt_schema
I will be preparing a .zip file containing the plugin. To install it you only need to unzip it in the vendor/plugins directory. A great guide to using plugins can be found here
acts_as_taggable provides a very easy means for tagging various objects in your Rails application. By using this plugin you can now add a tag to every object and even look at those objects from the tag's point of view; thanks to :polymorphic => true.
I was entertaining the idea of using tagging in a system that I am intending to build. There would be a very generic framework that consists of a certain basic element that can be tagged by different types of tags. I then realized that this was actually the opposite of what acts_as_taggable does!. acts_as_taggable defines a single tag type that can be applied to any object. This lead me to thinking, why not join both ideas? And hence the acts_as_taggable_tag.
acts_as_taggable_tag is not yet a module, but I couldn't resist the name ;). What it does is that it simply enables any object to act as a tag for another object even if it was of the same class or even if it was tagging itself! Thus implementing dynamic many to many associations across all your persistent domain objects through tags
The caveat though is that has_many :through does not play nicely with polymorphic associations as explained here . I ended up using only the join table (taggings in my case) and adding methods for retrieving both the objects that act as tags for the current object and the objects that are tagged by the current object
This implementation is a bit lacking when it comes to performance. What would make it sweet though is to enable :polymorphic associations with a has_many :through, looks like the next thing to dig into :)
Now for the code:
Person Class
class Person < ActiveRecord::Base
has_many :tag_joins,
:class_name =>"Tagging",
:as => :tagged_one
has_many :tagged_one_joins,
:class_name => "Tagging",
:as => :tag
def tags
self.tag_joins.collect { |tj| tj.tag }
end
def tagged_ones
self.tagged_one_joins.collect { |tj| tj.tagged_one }
end
end
Message Class
class Message < ActiveRecord::Base
has_many :tag_joins,
:class_name =>"Tagging",
:as => :tagged_one
has_many :tagged_one_joins,
:class_name => "Tagging",
:as => :tag
def tags
self.tag_joins.collect { |tj| tj.tag }
end
def tagged_ones
self.tagged_one_joins.collect { |tj| tj.tagged_one }
end
end
Tagging Class
class Tagging < ActiveRecord::Base
belobgs_to :tag, :polymorphic => true
belongs_to :tagged_one, :polymorphic => true
end
The code in the Person and Message classes is identical, now each of them has a list of tags and a list of tagged_ones each containing whatever objects of whatever classes that happen to tag or be tagged by the current instance of Person or Message
The Tagging class represents the double polymorphic association between the tagging object and the tagged one regardless of the Class. This approach can be used to implement all sorts of hierarchies among your persistent objects through tagging. I am using it to build a multi process project management tool, which through tags can create different views of the same data like an Iteration/Story one for something like XPlanner or a TodoList one for the BaseCamp style.
Happy tagging :)
Posted by oldmoe Friday, April 14, 2006 4/14/2006 03:00:00 PM
For the past few months I was surrounded by more white collars than ever in my life. Mr. IT Manager this and Mr. IT Manager that, even Mr. Director of Technical and Non Techncial Economic Hyper Relations at PSGCD (their names happen to puzzle you somtimes, dont they?).
And guess what? meetings to these people are like water to a fish, they can hardly survive out of the meeting room. We need to change a label? let's throw an ultra high level management meeting for three companies and let everyone and his brother join in. Now after 3 hours of brain trashing everyone agrees that the label really needs changing, and a follow up meeting is set to decide on the actual label to be used (ofcourse that's an exaggeration so take it with a grain of salt,...or two!)
I just heard a comment saying the I should be doing more meetings and less programming! I beg your pardon, programming is MY WATER! I dont imagine myself laying back and moving things by pointing a stick at them. I'm not an ivory tower type of a manager either. And I dont like the constraints that conventional management and white collar cultures are putting around me.
The joke is, after all that, those white collars are COSTING US MONEY! you'd imagine that working with multinationals is like having a cash cow, rather it's like having a fake cow for display (and no milk at all). You only have to deal with complicated requirements (that dont even make sense sometimes), very eager expectations, very slow payment and very limited technical assistance (up to the level of requiring us to travel between cities to install files).
I bet you're asking now: "why are you putting up with all of this?", I just asked myself the same question, and hence that (seemingly whining!) post :)
I'm still pondering it all in my mind, where should we (or I?) be directed, how can we be happy? and I mean HAPPY!
I'd quote DHH here, "so be happy"
- No remote includes!
It would be great if one could just include a url in a template. But how could this be done? and the latency? these are questions that arise when speaking about such a feature - InnerHTML based!
since they always return a string representing the rendred template the only reasonable way to use it is to set it as the innerhtml of some html node in your document. While this is a normal practice it has some drawbacks! - once you set the inner html of an object the control is immediately returned before the content is actually parsed and added to the dom tree
- so if you want to access an element just after the template was rendered you might find it not yet available as a dom node!
- While every benchmark on that issue says that setting innerhtml is the fastest thing on earth I believe that dom manipulation is much faster (the benchmarks count only the time needed to create the string and assign it to the node, not the parsing and rendering time!)
The first is really not that important. It is the second one that bugs me! I think one solution would be to parse the rendered template yourself and create the nodes as you go and after you finish you append them to the container. But this would be much slower than the browser's implementation! An alternative would be an event that fires when the browser finishes the rendering! would that be possible? I think it could be done one way or another (like every render function returns a unique id of an object it appends to the end of the template. This is looked for on a timeout or interval method which would fire some callback if it is found).
Just some thoughts. Aside from that JSTemplates are ultra cool!
Posted by oldmoe Friday, September 30, 2005 9/30/2005 12:04:00 AM
In an attempt to trace the program flow of our Javascript client application we needed a stack trace of function calls. We needed to eliminate unneeded redraw and keep the code flow intact.
Over at deep some amazing AOP stuff in Javascript can be found. So what we did to print the stack trace? only an advice is added before all the interrested functions (Javascript has enough reflective abilities for us to be able to inject these advices along our object graph dynamically). And now we have a nice and accurate call stack! Ofcourse many cases of redraw are gone now. And we introduced another feature into our Javascript beast, AOP capabilities!
Of particular interest where the SpringMVC and the RubyOnRails frameworks. Both provide means for view handling. And both provide hooks for custom view handlers. Which would lead to the interesting question.
What's wrong with the current view handlers?
All these handlers operate in the classical web application linked pages model (Web 1.0). With the eager move to Single Page Applications (Web 2.0) we need to add these abilities to current frameworks (or create new frameworks?)
How can we acheive this?
Instead of sending complete pages. The server should send to the client data only. And this data should only be view data with no control data. The view control data should reside completely on the client. So instead of responding to a list_items request with a rendered html page with the items required we will only send the data required. To be parsed and rendered by the client browser.
And data format?
Although it seems natural that we use XML for data sending. In a web browser environmnet it is more fit to use a more Javascript friendly approach. JSON (which is very XML like but in native Javascript) can be used as a data interchange medium. JSON will easily be parsed into live Javascript objects. Passed easily to templates (Javascript Templates from trimpath). And used to store model cache if needed (keeping the cache in the native Javascript format rather than XML is crucial for performance).
Template Engine?
As mentioned above. The Javascript Template from www.trimpath.com act as a very good alternative to its server side cousins. It has Smarty, Velocity like syntax. And it is very extinsible. The only draw back is that it is lacking an include mechanism (though this is partially solved by using its Macros)
And the result?
Adding web 2.0 application capabilities to the current tried and true web application frameworks. Making it easy to move even current apps to the user friendly era of web 2.0. I will be working soon in the Spring to JStemplates interfaces and I might delve into a RoR implementation as well (this would be very easy I believe, might envolve overriding a method or two in the action controller). The only thing that is missing is standardizing the client side controller code.
be more active in the coming months. And I will try to make this Ramadan the best so far. Amr is the kind of man that is able to rally people for a good cause. He is our Gandhi! And more!. I just wish we all give him all the support we can. May Allah reward us all. Amen
Posted by oldmoe Saturday, September 17, 2005 9/17/2005 12:58:00 AM
His worries are very well founded and stem from the fact that current web application frameworks are mostly server centric. The server is playing a monoply on the data model, action control and even view state.
Here's a look from the other side of the fence. I was not doing much Java development as of late and instead of using bell-and-whistle based frameworks I was participating in a big project that had the following properties:
- Huge client base that consumed the processing power and the bandwitdh of the server(s)
- Distributed implementation which should be done as simple as possible due to time constraints
- Distributed data set (per user data are private and thus caching has no real system wide effect)
- To stress the first point. Processing and bandwidth were at premium! we had to save on both.
Recipe for success:
- Build a stateless server application (only loggedIn? should be tracked)
- The server only responds to fine grained actions (for some actions the server responded with JSON data, for most of the action the server simply said: "ok")
- Put this app on as many servers as you wish as long as they share the data and can share the loggedIn? peice of info.
- Build a statefull SPA client:
- Full model and model cache (use the cache to avoid visiting the server often)
- Full fledged controller (the client decides what happens next)
- Complete View rendering and management (Javascript templates any one?)
- Connect everything using Ajax and JSON (how thinner can we get?)
- Forget about HTMLUnit (use your own Javascript infrastructure to test and direct your DOM)
- Fast prototyping
We managed to get a prototype (no server side thing) up and running in no time. Even during development the evolving prototype would be one step ahead by relying on static data rather than server responses - Prototype reuse
The prototype actually became the application! It was continously under refactoring but the team managed to always keep it under control - Real Distributed Application
The clients now do the controlling part and the parsing and rendering of view templates. They keep the view state and the model state (through the cache) only updates are propagated to the server. Much less processing on the server(s) now - Bandwidth Savings
Down to 25% of the original bandwidth consumption (more savings expected) and due to decreased server load we are now able to use mod_deflate for even better savings
Posted by oldmoe Wednesday, September 14, 2005 9/14/2005 11:32:00 PM
Here's an example from developer.apple.com :
var req;
function loadXMLDoc(url) {
req = false;
// branch for native XMLHttpRequest object
if(window.XMLHttpRequest) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
// branch for IE/Windows ActiveX version
} else if(window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
if(req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send("");
}
}
function processReqChange() {
// only if req shows "loaded"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
// ...processing statements go here...
} else {
alert("There was a problem retrieving the XML data:\n" +
req.statusText);
}
}
}What's the problem with the above code? specially in an asynchronus environment?The variable req (which is used as the XMLHTTPRequest object) is defined globally!! So when our friend tries to instantiate a new request before the current one finishes it will overwrite it and you will lose any reference to the old request.
But why did we have a global variable in the first place? If it's so bad, why didnt we make it local tothe loadXMLDoc funciton?
Simple, because the call back function needs to access this variable. And if it cannot be found in its scope it will look at it in the global scope.
Closures any one?
this can easily be solved by defining the call back function as an inner function to the loadXMLDoc() function and declaring the variable req as local to this function
function loadXMLDoc(url) {All we needed to do is declare req as a local variable by adding the var keyword and removing the global declaration.
var req = false;
// branch for native XMLHttpRequest object
if(window.XMLHttpRequest) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
// branch for IE/Windows ActiveX version
} else if(window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
if(req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send("");
}
var processReqChange = function() {
// only if req shows "loaded"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
// ...processing statements go here...
} else {
alert("There was a problem...");
}
}
}
}
This way each request will spawn a new req object which will be accessed by the callback function and will be garbage collected after the callback function returns (some browsers - namely older versions of internet explorer - might leak memory here as they deal very badly with closures)
The Egyptians voiced their opinion! and what I conclude for it is :
- They dont care for the 19000 wrongfully imprisoned fellow Egyptians
- They dont care for being poisoned with illegal pesticides
- They dont care if their childern recieve no education at all
which calls for an interesting question, what do the Egyptians care for?
Posted by oldmoe Monday, August 22, 2005 8/22/2005 01:48:00 AM
- While using iframes I had to manage my call backs from within the returning page (i.e. server output needed to know about the client side control structure, and needed to resend some post operations data other than a mere success or failure message). Now it's totally client side! Using callback functions (with their closures! you dont get to lose the request initiating environment!!) is so very sweet to work with!
- Handling server misbehaviour!. You dont rely on the iframe to always load any more, you can easily detect when a request times out (without writing your special timing routines for that!)
- All you communicate is data now. And I even dont use XML, JSON is my winning card here and it integrates oh so easy with the front end logic (our back end code is written in C, thanks for whoever wrote libjson, a life saver for us).
