MTJ Hax

My web site looks better in IE than Firefox

Posted in software by mtjhax on March 4, 2010

I noticed something unexpected today. A web site I’m working on looks better in IE 8 than it does in Firefox 3.6. It’s not because I did a bad job and the layout is significantly different on the two different browsers–they are almost identical down to the pixel. It’s the subtle things like how text, graphics, and default controls are rendered. Sadly, I can’t just direct you to the site to check it out for yourself because most of it is a private demo, but the following are some tiny screenshots taken out of context to illustrate my point.

Text Rendering

This obviously comes down to personal preference, but in general I think the anti-aliased fonts used by IE make for a more professional-looking site with less effort, using default fonts. Ironically I prefer un-smoothed fonts on my desktop, maybe because I’m an old-school DOS/Windows 3 guy. I have read that enabling font-smoothing in Windows will cause Firefox to use the anti-aliased fonts (along with everything else on your system) but regardless of how I change that system setting, the Firefox text does not seem to be smoother and the IE text is the same. I think IE must have its own font rendering engine aside from Windows ClearType. Example:

Screen snapshot of text rendered by Firefox

Firefox text


Screen snapshot of text rendered by Internet Explorer

Internet Explorer text

Default Controls

When using default controls like buttons, the anti-aliased text sometimes gives the buttons a more polished look. Obviously, most web sites these day use custom control with their own graphics to achieve that too-kewl post-Web 2.0 look, but when you’re tacking together a quick site it’s nice to rely on good old default controls, at least to get started:

Screen snapshot of Firefox default buttons

Firefox buttons

Screen snapshot of Internet Explorer buttons

Internet Explorer buttons

Image Scaling Quality

The application I am working on allows the user to zoom images. Since the user is very likely to look at the 100% zoom view, we just send the full-size image and use Javascript to resize, letting the browser do the scaling work. While the images look exactly alike at full zoom, I have noticed that in some photos–mostly dark and grainy ones–IE’s graphics scaling is much smoother than Firefox. In the following example image, which is being scaled to 20% of original size by the browser, it almost looks like Firefox converted it to a dithered GIF:

Screen snapshot of an image scaled to 20% normal size by Firefox

Firefox scaled image

Screen snapshot of an image scaled to 20% normal size by Internet Explorer

Internet Explorer scaled image

I’m not likely to change to IE because of these minor features, but it’s nice to see that there’s at least one or two good reasons that Microsoft’s beast is slower at rendering pages than the competition. In case you are curious how Google Chrome stacks up, in a nutshell its text and controls look more like Firefox, and its image scaling must use the same engine as IE because they appear identical. Speed-wise it’s no contest. Chrome loads and renders the fastest in my unscientific tests, but partly that’s because there’s so little going on–I don’t have AdBlock and my four other add-ons running in the background under Chrome. Subject for another post.

Is soft-delete fail?

Posted in code, ruby on rails by mtjhax on March 1, 2010

Brakeman by artist Will Enns, willenns.com
It appears that the Rails community has hit the brakes hard on soft-delete (marking database records as ‘deleted’ instead of actually deleting them). Is the entire concept of soft-delete fail? Long before the Rails community embraced soft-delete, there were discussions of its various pros and cons.

I’m a big believer in the concept of never permanently deleting data. It’s a pretty easy bandwagon to jump on–why risk losing data when you can safely tuck it away somewhere or simply mark it deleted, then update all of your database queries to ignore the “deleted” rows? As a believer, I’ve worked quite a bit with soft deletion in both web and stand-alone apps, and it has always been an ongoing maintenance headache. Everyone in the organization who touches the data ends up needing to be aware of the soft-deletion scheme to avoid causing problems–programmers, DBAs, even support staff. Even if you move deleted records into a separate table, you are still likely to end up with a system where one forgetful programmer can cause headaches for everyone.

For a while it looked like Rails to the rescue, first with technoweenie’s (Rick Olson) acts_as_paranoid which started as an experiment back in 2005, and more recently semanticart’s (Jeff Chupp) is_paranoid, which cleverly exploited the new ActiveRecord default_scope feature to improve on the concept. Over the years, there has been some criticism of acts_as_paranoid’s problems and limitations but devotees have worked around them. More recently, some plugins and gems have popped up with alternative schemes such as moving deleted records to an archive table instead of marking them as deleted. In the last few months, however, Rails community support for soft deletion seems to be having a meltdown. Jeff of semanticart publicly stated that he is killing is_paranoid and rumored that technoweenie is no longer using acts_as_paranoid. A quick search of the net finds a number of very recent articles slamming soft-delete in general (for example, see Luke Francl’s list of softdelete Bookmarks) and there are an increasing number of Rails add-ons dedicated to the concept of moving deleted records to an archive table.

Ultimately, the argument for killing is_paranoid boiled down to how making soft-delete transparent added one too many layers of Rails magic, shielding the programmer from important data considerations that should have been kept explicit. Soft-delete doesn’t get off the hook that easily though–it still presented problems even when wrapped in Rails magic. For example, you can get into trouble if you use unique indexes in your database. Imagine a row is soft-deleted, then you try to create a new row with the same attributes that were supposed to be unique. Also, as Rails has rapidly evolved new and interesting methods of associating and finding data, the soft-delete add-ons required constant updating.

Personally, I’m starting to think the best answer is to remove all consideration of soft-delete / archive-delete from your apps and handle this externally. There are quite a few tools out there for backing up data in real-time as it is created, using SQL triggers so deleted rows are automatically journaled or moved into an archive, etc. There are also some pretty compelling articles asking an important meta-question, why are we deleting data at all?

For further reading with some interesting suggestions for alternatives, I recommend Luke Francl’s post on Rail Spikes.

If you use Rails, take a few minutes to learn Ruby

Posted in ruby on rails by mtjhax on February 9, 2010

I’m sure I’m not the first person to code three or four complete Ruby on Rails projects before bothering to learn the finer points of Ruby, but after looking at some old code I’ve gotta say I wish I had spent a day just reading Pickaxe or Why’s (poignant) Guide before I jumped in with both feet.

There are dozens of examples of my former Ruby stupidity in virtually any module on my hard disk. They mostly fall into the category of “doing things the C++ way instead of learning the Ruby way”. For example:

  if !my_param.nil? && my_param.is_a?(Integer) && my_param >= 0 && my_param < 10
    # do stuff
  end

Tsk tsk. Terrible mess. Imagine an entire module full of that. The following is just so much better:

  if (0..9).include?(my_param)
    # do stuff
  end

This one method properly deals with nil and non-integer values. Nice!

Of course, I don’t blame C++ for my cluttered code — one could easily create their own syntactic sugar in C++ for common tasks like parameter-checking. The point is that Ruby has all these cute tricks already, and you should be making your life easier by using them!

If you happen by this post, I invite you to share your own favorite Ruby tricks.

Tagged with: , ,

Nginx gzip config and image problems in IE

Posted in web hosting by mtjhax on February 2, 2010

I am running a web site under Nginx and noticed that when you right-click images on that site using IE, even if they are obviously JPEGs you can only save them as bitmaps (.BMP). If you get properties on the image the type comes up as unknown and the last modified date is missing. I tracked this down to my Nginx gzip configuration, specifically the option gzip_vary on;.

I won’t discuss what the gzip_vary option is supposed to do, partly because I don’t know and partly because I don’t care — it’s supposed to help tell browsers that gzip compression is available on the server or somesuch. After a little trial and error I realized that if I disabled this option, IE started treating my images normally. Apparently, if IE sees a HTTP response header that it doesn’t understand or is in the wrong place, it pitches a hissy fit and skips some of your headers. WTF.

I am using Nginx version 0.7.61. After reading the release notes of Nginx it appears this may have been a bug that is now fixed. One release mentioned that the gzip_vary header was being sent at inappropriate times. I can’t update that server right now because it’s on the critical path for a project at the moment and needs to be stable, but I will repost if updating Nginx solves the issue.

Tagged with: , , , , ,

Restart your Phusion Passenger app from a web page

Posted in ruby on rails, web hosting by mtjhax on February 2, 2010

Phusion Passenger has a cute way of letting you restart your Rails app instead of restarting your web server. You just create or touch the file myapp/tmp/restart.txt. If you want to do this from inside your Rails app (maybe you’re tired of typing ‘touch tmp/restart.txt’ in a SSH prompt) just do something like this in a controller:

def restart_server
  FileUtils.touch(File.join(RAILS_ROOT,'tmp','restart.txt'))
  render :nothing => true
end

Just make absolutely certain that this action is not accessible from the outside world (administrators only), can’t be invoked accidentally by a redirect (put in a confirmation dialog?), and can’t get involved in any sort of infinite loop (don’t put anything like this in a homepage controller action). Obviously it doesn’t have to be in a controller, but when you’re sawing off the tree branch you are sitting on, it’s nice to be near the ground (i.e., closer to the top of the call stack).

Administrating restful-authentication users with ActiveScaffold

Posted in ruby on rails by mtjhax on January 16, 2010

Needing a quick set of admin pages for a site that uses Restful-authentication, I decided to use one of the many nifty scaffolding tools for Ruby on Rails to generate some customizable views. I settled on ActiveScaffold for reasons I will mention later.

The normal way you use ActiveScaffold is to simply add an ‘active_scaffold’ declaration to your controller that causes views to be dynamically generated. For example:

class UsersController < ApplicationController
  active_scaffold
end

The problem is that this code adds actions such as show, list, and create to your controller that are almost certainly already in use by your user-facing web site. The obvious answer is to create a separate controller and configure ActiveScaffold to  specify which model to use. Easy enough, but there are a couple useful tricks worth considering.

Why ActiveScaffold?

ActiveScaffold caught my eye for a number of reasons including the fact that their main page has links directly to their competition in case “ActiveScaffold isn’t what you’re looking for”. I’m not sure if that’s a friendly open-source attitude or a bit of a boast–maybe a little of both–it’s all good either way. Hobo and Streamlined both looked really cool but were a bigger investment in learning curve than I cared to make that afternoon. Lightweight is always good when patching stuff on to an existing site and refactoring isn’t in the budget.

An interesting feature of ActiveScaffold is that the views are dynamically generated and customized through configuration options, helpers, and CSS (as opposed to code being emitted and then customized directly). There seems to be a minor movement in some Rails circles away from code generators towards configurable, customizable plugins/gems. Another example is authlogic, the hot new flavor for user authentication in Rails. This all seems a little contrary to Rails doctrine (convention over configuration) but the configuration is lightweight and it reduces the amount of cruft I have to add to my code that is tangential to my application’s purpose. For a plugin that makes a ton of sense.

Approaches

One interesting approach to make a user admin interface is to use the map.namespace feature in routes.rb. With this you can create restful routes such as /admin_users, /new_admin_users, /delete_admin_users, etc. Something like this:

map.namespace :admin do |admin|
    admin.resources :users
end

Then you can create new admin controllers and views in app/controllers/admin, app/views/admin/users, etc. This is a great pattern but it’s a little tricky to implement with something like ActiveScaffold that is automatically generating your views.

The simpler approach with ActiveScaffold is to simply create a new controller, say AdminUsersController, and specify the model name in your active_scaffold configuration:

active_scaffold :<your_model_name>

There’s only one hitch. Everyone in Rails these days is pushing the “thin controller, fat model” doctrine. What happens when you need special logic only available to administrators? Obviously you can add to your controller, but you might not be able to access everything in your model to provide this special behavior without opening some potential security holes. The best way for me turned out to be subclassing the User model as well as creating a new controller.

Implementation

In my case, I wanted to automatically activate users that are created by the admin without sending activation emails.

Step 1, subclass your normal Users model in models/user.rb:

# model subclass only used by admin backend
class AdminViewUser < User
  # override method instead of using callback macro to prevent super from being called
  def before_create
    auto_activate
  end
  # (optional) skip post-activation email for users created by admin
  def recently_activated?
    false
  end
protected
  def auto_activate
    @created = false  # (optional) skip sending signup email for users created by admin
    self.activated_at = Time.now.utc
    self.activation_code = nil
  end
end

Step 2, modify models/user_observer.rb to checks for automatic activation before sending out emails (optional):

class UserObserver < ActiveRecord::Observer
  def after_create(user)
    UserMailer.deliver_signup_notification(user) if user.recently_created?
  end
  def after_save(user)
    UserMailer.deliver_activation(user) if user.recently_activated?
  end
end

Step 3, add a new controller that uses ActiveScaffold, restricted to admin users only, in controllers/admin_view_user_controller.rb:

class AdminViewUserController < ApplicationController
  before_filter :admin_required  # see authenticated_system.rb below
  active_scaffold do |config|
    config.label = "My Fancy User Admin Page"
    config.columns = [:login, :email, :password, :password_confirmation, :created_at, :updated_at]
    # exclude some columns from specific views, for more config options see ActiveScaffold web site
    list.columns.exclude :password, :password_confirmation
    show.columns.exclude :password, :password_confirmation
    update.columns.exclude :created_at, :updated_at
    create.columns.exclude :created_at, :updated_at
  end
end

Final step, implement the :admin_required method. I added a column called ‘is_admin’ to my users table and created some new methods in lib/authenticated_system.rb:

module AuthenticatedSystem
  protected
  def is_admin?
    logged_in? && current_user.is_admin?
  end
  def admin_required
    is_admin? || access_denied
  end
end

This is just an example to get you started. There are probably security enhancements that could be made and, obviously, you can administrate models other than just User. In my version I also added a UserGroup model that is also administrated by ActiveScaffold.

Comatose CMS for Ruby on Rails

Posted in ruby on rails by mtjhax on December 20, 2009

Anyone who has built a Rails app and wanted to use a CMS just for the static portions of the site knows that there is no easy solution, especially when the CMS and Rails app need to share things like user logins. This issue is well-described by Aaron Longwell in his June ’09 blog post, The Ruby on Rails CMS Dilemma.

I started playing with different Ruby on Rails CMS offerings to learn more and try to come up with some new approaches. One really interesting little project popped up called Comatose CMS, initially developed by cartoonist/programmer Matt McCray. Comatose has a couple of sweet features I feel like every CMS should have:

  • Comatose runs as a loosely-coupled plugin to your Rails app (as opposed to being the base upon which you need to layer your app). If you want Comatose to generate a page, you just route the request to Comatose with some entries in your routes.rb file. If you want your app to handle the request, route it normally. Simple!
  • Comatose pages can be rendered inline similar to Rails partials. Imagine you have an entire app already built and you need a quick way to allow someone to edit specific chunks of content without moving entire pages into your CMS. Comatose inline rendering could have you there literally in minutes.
  • Comatose can use the same layouts as your regular app.
  • Comatose uses a simple callback pattern to use your app’s authentication and access control as its own. You can configure Comatose to mix-in any modules it needs (like the restful-authentication lib), then set up callback procs to control access to both the generated pages and the back-end editing interface.

I guess the main drawback to Comatose is that the original author has not worked on it recently and the two or three other committers have not made any changes in nearly a year. It does work with the latest Rails 2.x though. With more activity you might see better documentation, more examples and design patterns, maybe a few new features. Also, with a project this inactive, one worries that there are security flaws, performance issues, or other difficulties yet to be discovered. Unless I find another CMS with similar features, I’m thinking of giving it a go in a real project and seeing what I run into.

 

UPDATE October 2010: I realized that there is one major drawback to working with this CMS at this point — it was written to use an older, obsolete version of the Liquid markup language that is embedded into the app. If you install a more recent version of the Liquid GEM for any reason (I tried 2.0.0 and 2.2.2) it will cause Comatose to crash. Of course, someone with a little more free time than I have could fork the Comatose project and update it to use the latest version of Liquid, but this project is looking pretty dead. I guess full-fledged CMS apps like Radiant have officially won the day. Shame there isn’t more interest in this type of hybrid solution.

 

Here are a few Comatose CMS tips:

 

By arranging your routes in different ways, you can have Comatose handle everything, only specific pages, or any page not otherwise explicitly routed. I prefer that last style — just use resource maps and named routes for everything in your app, remove the default :controller/:action/:id routes, and add something like map.comatose_root "" at the end. For example:

map.resources :users
map.resource :profile
map.comatose_admin
map.comatose_root '', :layout => 'application'

A request for anything except /users or /profile will fall through to the CMS. If the CMS cannot find the request page it will render it’s own “404 not found” page (which you define by creating a page called “404” at the top level of your page heirarchy).

 


 

Comatose routes can be used like named routes in the app. For example, one might link to an /about page with comatose_root_path(:about). Sub-pages in the heirarchy can be accessed one of two ways, with an array of strings or symbols, or a string argument to the helper: comatose_root_path([:legal, :privacy]), or comatose_root_path('legal/privacy').

 


 

If you don’t like the look of comatose_root_path(:about) and comatose_root_url(:about) as helpers, you can make a shorter route such as:

map.static ':page', :controller => 'comatose', :action => 'show'

Then use static_path(:about) and static_url(:about) instead. The only drawback to this approach is that sub-pages will end up with ‘%2F’ (the entity for ‘/’) in your browser’s address bar.

To cause errors to be generated instead of 404 pages (maybe useful for debugging during development?) you could limit the possible values for :page as follows:

map.static ':page', :controller => 'comatose', :action => 'show', :page => /about|help|contact|legal\/privacy|legal\/terms/

 


 

For Comatose to use your app’s layouts, any helpers or modules used by the layout need to be included in the Comatose controller since it doesn’t inherit ApplicationController. This is easily configured in environment.rb:

Comatose.configure do |config|
config.helpers << :layout_helper    # my layout helpers
config.includes << :current_user  # my module that includes a current_user method

 


 

Finally, here’s some code for a layout that provides an “Edit this page” link for anyone who is logged in as an administrator:

<% if current_user && current_user.is_admin? && @page && @page.is_a?(Comatose::PageWrapper) %>
<%= link_to("Edit this page", "/comatose_admin/edit/#{@page['id']}") %>
<% end %>

Notice that @page is defined in the layout when Comatose is handling a page. It contains all sorts of useful information to take advantage of in your layout.

Tagged with: , , ,

free subversion hosting

Posted in web hosting by mtjhax on December 6, 2009

I have been trying out different Subversion hosting services lately, including ProjectLocker, Unfuddle, and XP-Dev. Here’s a quick critique.

ProjectLocker

ProjectLocker offers the most service for each price level by a wide margin. Even free accounts include 500 gigs, 5 users, unlimited repositories, unlimited bandwidth, and SSL access. Extremely generous compared to their competition. To some extent they are just a thin wrapper on top of Subversion, Git, and Trac but I consider that a good thing–I don’t really need my fancy XP/Agile tracking and collaboration tools on the same web site as my source control–I have things like PivotalTracker and Rally for that. Their site looks slightly clunky and old-school (like a pre-Web 2.0 site that got a quickie Arial Rounded makeover) but it works well and gets the job done.

My only gripe with ProjectLocker is the frequency and duration of their scheduled maintenance outages. Your mileage may vary, but I’ve only been using them about three months and already had my repositories offline for extended periods twice–they just finished a 42-hour scheduled maintenance (which is why I’m wasting time blogging instead of working), and about a month ago they did an 18-hour maintenance. I have no idea what they could be doing in all that time–in 42 hours you could build new servers from the ground up starting with nothing but a credit card, a TigerDirect catalog, and FedEx. If I had a $30/month account with them, this would be a real deal-breaker. As a free user I have to admit I’m getting an amazingly sweet deal though.

XP-Dev

I don’t have a lot of experience using XP-Dev. I started a project there and five minutes later there appeared to be an unplanned outage and I couldn’t get to my repository. To their credit, it was fixed minutes after I informed them but my partner on that project decided to move the repo to another provider rather than risk any more downtime. XP-Dev has a pretty slick site with nice, simple tools. Subversion, Trac, nice integration features (Basecamp, FogBugz, Lighthouse, Twitter, etc.), collaboration tools (wiki, blogs, forums), and some basic XP/Agile project support–I guess the project tracking and collaboration are useful if you are doing them both on the site and already spending time there–might as well get it all done in one place. Feature-rich and reasonable pricing structures. I get the distinct impression XP-Dev is a tiny 1- or 2-person organization, which isn’t necessarily bad, but isn’t very comforting either. Despite this, I hope to try them out again soon and see how their reliability fares, at least anecdotally.

Unfuddle

Unfuddle is the third Subversion hosting service I have tried recently. So far so good in terms of reliability. Their pricing scheme seem a little stingy on the disk space side — free accounts get 200MB of space and only 1 project, and $99/month gets you only 10GB of space and 50 projects. Of course, very few people using a hosted solution actually need more than 10 gigs and 50 projects, but I wouldn’t describe it as “enterprise” space. The site is slick and pleasant to use. I haven’t had the chance to try out the site tools much yet–they have some relatively simple milestone-based project tracking, messages and “notebooks”, and a nice dashboard feature that lets you know various things that are going on with the project.

Conclusion?

The problem with the value-added features of all of these sites is that they don’t seem robust enough to convert me from just opening a browser tab to a more fully-featured alternatives. For example, I’m really liking PivotalTracker for project tracking — full-featured Agile support with a super-slick, interactive interface that keeps things simple and saves time.

I don’t have a clear winner yet, but I have definitely concluded that reliability and uptime (whether scheduled or unscheduled) are the most important features–everything else is pointless if I’m trying to get work done and can’t get into my repository.

workaround for “Malformed version number string mswin32”

Posted in ruby on rails by mtjhax on November 11, 2009

People are reporting the error “Malformed version number string mswin32” when unpacking or using unpacked Ruby gems in their Rails project. I ended up figuring this out because I ran into it while trying to unpack the sqlite3-ruby gem and couldn’t Google any fixes.

Here’s the quick workaround (hack):

1. Look at your unpacked gems in vendor/gems and find any directories ending in “-x86-mswin32”. Change the directory name by removing “-x86-mswin32”.

2. Inside that same directory, there should be a file name “.specification”. Open that file with your text editor, find the line “platform: x86-win32” and simply remove the “x86-win32” so it just says “platform: “.

3. Your project should now load. But read on…

When gems are unpacked, they are placed in vendor/gems with directory names that include the gem name and version separated by dashes. e.g.: mechanize-0.9.3. As unpacked gems are loaded, Rails uses these directory names to get the version number of the gem. Comments in the Rails code lead me to believe that this is legacy support for older gems that preceded the use of .specification files. (I’m using Rails 2.3.4, the specific code is [rubydir]\lib\ruby\gems\1.8\gems\rails-2.3.4\lib\rails\vendor_gem_source_index.rb, lines 103 – 106, method version_for_dir).

When the gem is platform-specific, however, then the directory name is supposed to contain the platform name separated by a dash (per the GEM::Specification class). Since the gem loading code is looking for a version number at the end of the directory name and is instead finding a platform name, this results in the error “Malformed version number string mswin32”. (It should say “malformed version string x86-mswin32” but since this particular platform name contains a dash, which is the delimiter, it only picks up the last bit.)

This seems to be a Rails bug since different parts of the code are expecting different things at the end of the directory name, not to mention that gem names and platforms both commonly contain dashes which are the delimiter the code is using to find the version number.

After figuring this out I realized this begs the question: why am I unpacking a platform-specific gem when I develop on Windows and deploy to Linux? D’oh! So here’s the real tip — don’t unpack platform-specific gems kids. And remember to use hand sanitizer this Winter.

Tagged with: , , , ,

Hosting your app on GoDaddy from the site root

Posted in web hosting by mtjhax on November 6, 2009

A quick tip for those who are hosting a Rails app (or any app really) on GoDaddy or other shared hosting sites where the app ends up being hosted from a subdirectory, e.g. http://www.mysite.com/my_app/ instead of just http://www.mysite.com.

If you only have one application and want it to be hosted from your root URL, edit your top-level .htaccess file (where you see default GoDaddy files like welcome.html and missing.html) and add something like the following:

# EDIT: added first line so GoDaddy web stats won't break!
# if the requested URL doesn't start with /stats/ or /my_app/
# then add /my_app/ to the beginning
RewriteCond %{REQUEST_URI} !^/stats/.*
RewriteCond %{REQUEST_URI} !^/my_app/.*
RewriteRule ^(.*)$ /my_app/$1

If you are using Rails, you can now get rid of the relative_url_root configuration (usually in environment.rb or production.rb), so links will be generated like /images/mypic.jpg instead of /my_app/images/mypic.jpg (although both should work fine).

This probably works on other Apache-based shared hosting providers as well. I haven’t road-tested this solution completely but it’s very basic Apache stuff so there shouldn’t be any problems.

EDIT: Here’s an even better tip for those who are thinking of hosting a Rails app on GoDaddy shared hosting… don’t do it! Unless you just need a cheap site for sharing prototypes. You can get decent VPS hosting for $15/month these days–there’s no reason to have a slow site running on outdated Apache & FastCGI versions that mysteriously crashes about one out of ten page views just to save $7 a month. In GoDaddy’s defense, shared hosting is a fail concept of bygone days now that VPS technology has become so widespread and inexpensive–there’s nothing particularly wrong with their service that a few version updates wouldnt fix.