$(document).ready(function () { $('table').filter(function (index) { $(this).removeAttr("style"); $(this).removeAttr("rules"); $(this).removeAttr("border"); $(this).removeAttr("cellspacing"); $(this).removeAttr("cellpadding"); }); });
Thursday, 30 May 2013
Remove .net styling from GridView
Tuesday, 21 May 2013
Detect change of browser orientation with javascript
window.addEventListener("orientationchange", function() { //do whatever you need to do }, false);
Monday, 20 May 2013
Ensure a div is visible in mobile browser
Sometimes on mobile devices presentation of these can be problematic - the reveal of a new div may happen fully or partially off-screen, seriously damaging the user experience. To counteract this I built a javascript function which I use after each new reveal to check if the div is viewable in the browser:
function checkIfDivIsViewable(div) { var $window = $(window); var myTop = div.offset().top; // top of element var windowTop = $window.scrollTop(); // top of window var windowBottom = windowTop + $window.height(); // bottom of window if (myTop >= windowTop && myTop <= windowBottom) { // div is viewable in browser, do nothing } else { // div is not viewable in browser, so scroll it $('html,body').animate({ scrollTop: myTop }, 300); } }I tried calling this on completion of reveals, like this:
$('#divToShow').slideDown(300,checkIfDivIsViewable($('#divToShow')));but this didn't prove too reliable. I don't know why, but it's posssibly because the div is still in motion when we're asking jQuery to assess it's position. So at the risk of appearing a bit hacky I check the display after a small delay. This seems to work fine:
$('#divToShow').slideDown(300); //now check that the div we just revealed is visible setTimeout(function () { checkIfDivIsViewable($('#divToShow')); }, 500);
Tuesday, 7 May 2013
Rails Activerecord - common performance pitfalls
1. Model.find(:all)
In versions of Rails before 2.3, this is a memory killer. The most common form in the wild is:
Comment.find(:all).each{ |record| do_something_with_each(record) 1125;
If you have 100,000 Comments, this will load and instantiate all 100k records in memory, then go through each one. In Rails 2.3, the .each will paginate through the results, so you’ll only load in small batches, but this won’t save you from the following variations:
@records = Comment.all @records = Comment.find(:all) @record_ids = Comment.find(:all).collect{|record| record.id }
Each of these will load up all Comment records into an instance variable, regardless if you have 100 or 100,000 and regardless if you are on Rails 2.1 or 2.3
2. :includes are Including Too Much
Article.find(:all, :include => [:user => [:posts => :comments]])
This is a variant of the above, intensified by the one or multiple joins on other tables. If you only have 1000 articles you may have thought loading them in is not a big deal. But when you multiply 1000 that by the number of users, the posts they have and the comments that they have… it adds up.
3. :includes on a has_many
@articles.users.find(:all, :include => [:posts => :comments]])
Variation on the above, but through a has_many.
4. @model_instance.relationship
Referring to a has_many relationship directly like so:
@authors.comments
is a shortcut to the potentially bloated:
@authors.comments.find(:all)
Be sure that you don’t have thousands of related records, because you will be loading them all up.
5. Filtering Records with Ruby Instead of SQL
This is also fairly common, especially as requirements change or when folks are in a hurry to just get the results they want:
Model.find(:all).detect{ |record| record.attribute == "some_value" }
ActiveRecord almost always has the ability to efficiently give you what you need:
Model.find(:all, :conditions => {:attribute => "some_value"})
This is a simple example to make the point clear, but I’ve seen more convoluted chunks of code where detect or reject is using some non-attribute model method to determine inclusion. Almost always, these queries can be written with ActiveRecord, and if not, with SQL.
6. Evil Callbacks in the Model
I’ve helped a couple of customers track down memory issues where their controller action looked perfectly reasonable:
def update @model = Model.find_by_id(params991;:id]) end
However, a look at the filters on the model showed something like this:
after_save :update_something_on_related_model . . def update_something_on_related_model self.relationship.each do |instance| instance.update_attribute(:status, self.status) end end
7. Named scopes, default scopes, and has_many relationships that specify :include Where Inappropriate
Remember the first time you setup your model’s relationships? Maybe you were thinking smartly and did something like this:
class User has_many :posts, :include => :comments end
So, by default, posts includes :comments. Which is great for when you are displaying posts and comments on the same page together. But lets say you are doing something in a migration which has something to do with all posts and nothing to do with comments:
@posts = User.find(:all, :conditions => {:activated => true}).posts
This could feel ‘safe’ to you, because you only have 50 users and maybe a total of 1000 posts, but the include specified on the has_many will load in all related comments – something you probably weren’t expecting.
8. Use :select When You Must Instantiate Large Quantities of Records
Sometimes, in the reality of running a real production site, you need to have a query return a large data set, and no, you can’t paginate. In that case, the first question you should ask is “Do I need to instantiate all of the attributes?”
Maybe you need all the comment_ids in an Array for some reason.
@comment_ids = Comment.find(:all).collect{|comment| comment.id }
In this case, you are looking for an array of ids. Maybe you will be delivering them via JSON, maybe you need to cache them in memcached, maybe they are the first step of some calculation you need. Whatever the need, this is a much more efficient query:
@comment_ids = Comment.find(:all, :select => 'comments.id').collect{|comment| comment.id }
9. Overfed Feeds
Check all the places you are making XML sandwiches. Often these controllers are written early on and don’t scale well. Maybe you have a sitemap XML feed that delivers every record under the sun to Google, or are rending some large amount of data for an API.
10. Monster Migrations
Finally, watch out for your Migrations, as this is a common place where you need to do things like iterate over every record of a Model, or instantiate and save a ton of records. Watch the process size on the server with top or with “watch ‘ps aux | grep migrate’”.