RAILS_ROOT and Dir.chdir 1

Posted by Bob Silva Sat, 10 Feb 2007 21:29:00 GMT

It's been awhile since I've blogged on Rails, got diverted to a Novell to Windows conversion over Christmas break. A local community college requested emergency assistance with completing their conversion and asked us to do it in 2 weeks. My team and I got it done but it was a hell of a lot of work. Now that I'm back to programming, I ran into a little annoyance last night that took awhile to troubleshoot down. I'm working on a little app to provide a web interface to my book collection. My book repository is located elsewhere on my harddrive so I used Dir.chdir to move to the root of the repository.


To make a long story short, DO NOT use Dir.chdir in your Rails Applications. RAILS_ROOT, which is used to locate dependencies is a relative path. If you change the working directory of your Rails application, Rails may/will have problems loading dependencies. In my case, I used Dir.chdir to move to then iterate the publisher directories and add books found onto the publisher.books has_many association. This promptly failed with Publisher::Book uninitialized constant errors. The dependency loading mechanism uses RAILS_ROOT to find items in your load_paths and being a relative path, changes if you change the current working directory.


I believe one solution, which I didn't test, would be to set RAILS_ROOT to an absolute path in your environment.rb. The solution I chose was to not chdir but use a full path in each Directory or File operation i used. And now you know.

ActiveRecord Calculations 4

Posted by Bob Silva Sun, 05 Nov 2006 20:38:00 GMT

I've been wanting to modify the ActiveRecord Calculations module to support multiple aggregate calculations in one query for awhile now. My current project with lots of graphing finally gave me the motivation to do it. Refer to the patch for details and feel free to comment on it.
# Calculate different aggregate functions at once
min, max = Person.calculate(:min, :age, :max, :age)

# Specify the column aliases so you can order by it
views, replies = Posts.sum({:views => 'number_of_views'}, :replies, :order => "number_of_views desc") 
Multiple Calculations Patch

Deprecations in Rails 1.2 7

Posted by Bob Silva Thu, 02 Nov 2006 19:40:00 GMT

Here is a quick list of deprecations in Rails 1.2. Update your code now to make upgrading easier later. Some of these have actually been around since before the current release of 1.1.6 but I thought I'd mention them anyways.
Deprecations in Rails 1.2

* Passing method reference symbols to ActionController::Base#url_for. Use named routes instead.
* Calling ActionController::Base#render with string arguments. Ex: render('weblog/show')
* Using model, service, observer, depend_on and dependencies_on in your controller. Rails handles most dependency issues automatically now.
* Calling keep_flash instead of flash.keep
* Pagination will be extracted out into a plugin in Rails 2.0
* Auto complete will be extracted out into a plugin in Rails 2.0
* In place editing will be extracted out into a plugin in Rails 2.0
* start_form_tag and end_form_tag have been deprecated. form_tag still works the same, but there is no wrapper method for the . The preferred method is to use the new block form of form_tag. Ex: form_tag :action => :create do ... end
* The number helper method human_size is deprecated in favor of number_to_human_size
* link_to_image and link_image_to helpers are deprecated in favor of wrapping an image_tag in a link_to. Ex: link_to(image_tag(...), :action => :new)
* Using @cookies, @flash, @headers, @params, @request, @response or @session has been deprecated. Use the method form without the @. Ex: response.content_type
* Calling content_for :layout || :symbol. Use yield || yield :symbol instead
* Calling image_tag without an extension is deprecated. Auto appending .png will be removed. Don't be lazy.
* Passing :post as a link_to modifier to force a POST request via a link tag. Use :method => :post instead.
* push_with_attributes and concat_with_attributes on a HABTM association are deprecated. Use has_many :through with a join model instead.
* find_all and find_first on a has_many association. Use find(:all) and find(:first)
* :dependent => true on a has_many association. Use :dependent => :destroy
* :exclusively_dependent => true on a has_many association. Use :dependent => :delete_all
* When using :class_name on a belongs_to association, the foreign_key name will be inferred from the association rather than the :class_name. Therefor, use :foreign_key if using :class_name and they differ.
* A welcome deprecation, internally, the quote method of ActiveRecord has been renamed. After Rails 2.0 you will be able to use a column named quote again.
* Calling count on a model with string arguments for conditions and joins. Use an options hash instead. Ex: Model.count(:conditions => '...')
* Validations helper method add_on_boundary_breaking in favor of validates_length_of
* Hash extension create_from_xml has been renamed to from_xml
* All the old rake tasks have been moved to namespaced task, old ones are deprected. Ex: rake db:migrate instead of rake migrate

Rails Documentation 15

Posted by Bob Silva Wed, 25 Oct 2006 17:08:00 GMT

Improving the Rails documentation has been an ongoing discussion for awhile now. Over $15,000 has been raised by the community to improve them but nothing has come from that yet. I'm going to outline some of my thoughts about it here. These are just random thoughts and do not provide complete solutions. NOTE: This is not a rant against court3nay or the Rails Documentation Project. It's only my opinion on how it could have been handled differently. And for full disclosure, I did not donate any money to the project, I'd rather donate my time.


  • Not to take anything away from court3nay but the timing of the Rails Documentation Project was off for several reasons. First, you should have a game plan before you ask for the money. It's been many months now since everyone donated and nothing has been done with the money. This leads to other problems, mainly it breaks the trust of the community. The next time something worthy comes along, likely contributors will be a little more cautious before they hit that donate button.
  • With everyone waiting to see what comes from the Rails Documentation Project, the number of patches for documentation to core has dropped.
  • Has anyone answered exactly what is wrong with the existing documentation? I'll outline a few things I see:
    • Easily gets out of date, need to place documentation on the same level as tests when accepting or rejecting patches.
    • The API docs on rubyonrails.org need a version selector. People using older versions of Rails (for whatever reason), should be able to access the appropriate docs for their version. This problem will be even more evident once Rails starts shipping with OS X and other Linux distros. The public website should support this, not a message explaining how to generate local documentation for your Rails version.
    • No style guide. You have to refer to existing documentation to determine the style to use. Could be improved with a style guide.
    • Are they docs or an API reference? Currently, its a combination of both and both are lacking. I think the examples need to be expanded (and possibly even follow a common theme, more on this later) and the API's more complete. When rewriting the docs for some of the ActionView Helpers, I found a quite a few options that weren't documented.
    • Core should have a dedicated person(s) to handle documentation issues. Making sure patches are properly documented (or willing to do it themselves) and reviewing/committing documentation patches. This person should also create the style guide or written standard for documentation.
  • I see two different audiences for documentation. The new users who really need a tutorial like approach and the more experienced who really need an accurate API reference. I'm not sure the inline documentation in Rails can serve both audiences, but it can come closer than it does currently. I think it would be great if Rails shipped with a default example application that all the documentation examples pulled from and referenced back to. This maintains a common theme throughout and acts as an excellent teaching tool.
  • So what should be done with the $15,000? Pay an author (a good one preferably) to write an open source book on Rails. This book should be completed by the author and then maintained by the community to stay current with Rails development. This is the best way I can see to spend the money.
Update: Better explanation of the "open source book" idea.
By open source book, I mean a separately maintained API reference 
maintained in book form, organized into logical chapters and topics
with full API coverage and plenty of examples. Take the API docs 
for AR associations. ActiveRecord and its associations are documented
across many files making it difficult to provide full and complete
examples of its many use cases inline. Having a dedicated chapter to
cover each association with full example coverage and feedback 
capability would be better than the inline docs. The inline docs 
should provide full API coverage, but leave the examples out. This
other "officially" maintained documentation would contain the 
example coverage and many use cases as well as user feedback.

Yes this is extra work, but I think you could find plenty of 
volunteers, including myself, to make up an official rails-doc 
team with full commit rights to the documentation in rails trunk.

Planes, Cigarettes and Snowmobiles 2

Posted by Bob Silva Thu, 19 Oct 2006 00:59:00 GMT

It's an early Christmas for me this year. I'm heading off to RubyConf tomorrow and I picked up my new snowmobile last friday. Following through on my committment to stop smoking once my snowmobile arrived, if you need to find me at RubyConf, just look around the room for the guy with the cold sweats and convulsions, that will be fearoffish, he just quit smoking too, I'll be beside him passed out on the floor.

Cheers, hope to see you there!!

Testing Gotchas in Rails 6

Posted by Bob Silva Tue, 10 Oct 2006 04:07:00 GMT

I just spent the past several hours tracking down an issue with my tests. I use a combination of autotest from the ZenTest Suite and rails_rcov for my Rails testing. While testing using autotest, all my tests were passing just fine. I then went to check test-code coverage and all of a sudden I have a failing test.

* rake test:functionals:rcov - failed.
* rake test:functionals - failed
* ruby test/functional/clients_controller_test.rb - passed
* autotest - passed

So what gives? Basically, when running tests using the rake tasks, the test databases are recreated each time you run the task via rake db:test:clone. The real problem was that I was failed to load one of my fixtures in my functional test, but since the data existed from a previous test which used those fixtures, the tests passed. But when running the test from the rake task which resets all the tables, no fixture data existed in the database causing the difference in behavior between the two tools.

As always, if your tests are hokie, CHECK YOUR FIXTURES FIRST! Enjoy this tip.

MySQL Query Analyzer Rails Plugin 17

Posted by Bob Silva Thu, 21 Sep 2006 04:54:00 GMT

Anyone thats ever grown an application from a couple thousand hits a day to a couple hundred thousand has run into db optimization issues at some point. The best teacher is experience and Bravenet was a great teacher.

During Bravenets prime years, we had six load-balanced clusters, each with 5 webservers and one MySQL database server. Our userbase was partitioned over these 6 clusters. At peak times, our Queries Per Second on the database servers were over 900 on each. Each page made a minimum of 3-5 database queries (this was before caching became common place) and our traffic load was quite high, so high in fact that a poorly optimized PHP page or a non-indexed query would bring Bravenet down instantly requiring us to fix the script, re-commit and restart all the Apaches to come online. At one point, a nasty bug in our ad-serving code has us down for over 24 hours, it turned out that PHP was loading an array of over 10,000 elements on each request. As soon as we fixed it and committed the changes, Bravenet came back to life.

Premature Optimization

Fine concept for code but practicing it with your database is more akin to Immature Optimization. While it's common place to put indexes on your conditions columns and primary/foreign keys, sometimes (especially in Rails Schema) you just forget. If your writing a small application, you may never see the effects of your error, but as your application grows, it will quickly show itself by slowing down the load time of your pages.

MySQL Query Analyzer

MySQL Query Analyzer

With all that said, I wrote a plugin to make it easier to catch those mistakes and stay on top of your database optimizations. This plugin makes use of the EXPLAIN sql statement in MySQL to print out how MySQL formed its execution plan. Basically, for each SELECT query your application runs in the development or testing environments, Rails will also print the query execution plan right after it so you can quickly analyze the queries Rails is making and either add indexes, reorder your joins and remove unneeded or redundant indexes.

To install:
script/plugin install http://svn.nfectio.us/plugins/query_analyzer

Read the README for more information. Any feedback or improvements welcome. Good luck and don't be immature.

Tagging Your Test Cases 7

Posted by Bob Silva Mon, 18 Sep 2006 05:59:00 GMT

Here's a quick way to follow your tests a little more closely in your test.log. In your test setup, add the following line:
def setup
  RAILS_DEFAULT_LOGGER.debug "\n\e[0;31mRUNNING TEST CASE: #{name}\e[m\n"
end
When you run your tests, you will now see something similar to this.

Running Rails on OS X with MySQL 5.0.24 24

Posted by Bob Silva Tue, 12 Sep 2006 18:27:00 GMT

While installing all the pre-requisites for running Rails on my new Mac Pro, I ran across a problem with either the mysql gem or mysql 5.0.24 where it depends on 'ulong' being defined by the OS include files. On OS X it isn't, and this causes the ruby mysql gem to fail compilation which causes Rails to (possibly) throw a Lost Connection to MySQL Server during query error due to the fact that the built-in Ruby driver doesn't work with mysql 4.1+ authentication (edge rails fixes this limitation with the built-in mysql driver). A quick fix for this is to edit the ruby gem mysql.c and add the #define for a ulong so the gem can properly compile.
#define ulong unsigned long


Validate Dates in Your Models 3

Posted by Bob Silva Fri, 08 Sep 2006 06:11:00 GMT

mojombo in #caboose, just released his first version of Chronic, a new Ruby Gem for natural language processing of Dates and Times.
gem install chronic

irb:>
Chronic.parse('tomorrow')
    #=> Mon Aug 28 12:00:00 PDT 2006

Chronic.parse('monday', :context => :past)
    #=> Mon Aug 21 12:00:00 PDT 2006

Chronic.parse('this tuesday 5:00')
    #=> Tue Aug 29 17:00:00 PDT 2006

Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
    #=> Tue Aug 29 05:00:00 PDT 2006

Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
    #=> Sat May 27 12:00:00 PDT 2000

Chronic.parse('may 27th', :guess => false)
    #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007

The thing I find really nice is that if you pass it a malformed date, it returns nil. This means, in your model validations, you can add:
require 'chronic'

class Meeting < ActiveRecord::Base

  def validation
    errors.add :meeting_date, 'is not a valid date' if Chronic.parse(meeting_date.to_s).nil?
  end

end

Older posts: 1 2 3 ... 5