Pearware Blog : Category rails, everything about rails /category/rails.rss en-us 40 agile web development Reworking Net::SFTP to handle large file downloads <p>I&#8217;m writing an application that downloads access logs from our production servers and runs the <a href="http://awstats.sourceforge.net">AWStats</a> package against them to create the statistics web pages. This process is setup as a Rake task that uses the Net::SFTP library used by <a href="http://www.capify.org">Capistrano</a>, written by Jamis Buck. There is also a front-end Rails application to manage each of the applications to be retrieved. Everything was working great until I tried to grab a 550MB file from one of our servers. Net::SFTP chocked as it ran out of memory.</p> <p>It turns out that the command:</p> <pre><code> sftp.get_file log_file, local_file</code></pre> <p>ends up putting the whole file into memory, which is fine for small files, but not the large one that I was trying to download. Luckily it wasn&#8217;t too bad to refactor my class. Here&#8217;s the new code to achieve the same effect as the above <em>sftp.get_file</em> command.</p> <div class="CodeRay"><pre> stat = sftp.stat( log_file ) offset = 0 file_length = stat.size length = 64 * 1024 * 1024 File.open(local_file, File::CREAT|File::TRUNC|File::RDWR, 0644) do |f| while (offset &lt; file_length) sftp.open_handle(log_file) do |handle| data = sftp.read(handle, :length =&gt; length, :offset =&gt; offset) f.write(data) offset += data.length end end end</pre></div> <p>This downloads the file in 64MB increments, using only that much memory at any time.</p> Tue, 26 Jun 2007 14:13:00 -0500 urn:uuid:ec2657e4-c48c-4060-aa19-59894f59df7c http://blog.pearware.org/2007/06/26/reworking-net-sftp-to-handle-large-file-downloads#comments ruby rails ruby rails capistrano net::sftp rake http://blog.pearware.org/2007/06/26/reworking-net-sftp-to-handle-large-file-downloads RadiantOnRails released on RubyForge <p>RadiantOnRails is a <a href="http://www.radiantcms.org">Radiant</a> extension I created to allow a Rails application to co-exist with Radiant, giving the developer the best of both (dynamic and static) worlds. You can now visit the new <a href="http://www.rubyforge.org/projects/radiantonrails">project page</a> on RubyForge.</p> <p>This extension will be a major piece for the website I&#8217;m currently working on, <a href="http://www.realidaho.com">RealIdaho.com</a>. Most of the pages for the site just display static content about cities, but there are other portions of the site that will be fully-dynamic Rails pages. This extension allows me to combine both portions of the site into one application so we can develop the Rails pages for displaying the data-driven pages and still leverage the wonderful user interface created by the Radiant team. This will allow the realtor to make content changes without me having to make code changes and redeploy.</p> <p>RadiantOnRails currently allows Radiant snippets to be inserted into Rails views and the next step is to allow the Rails views to use the Radiant layouts so that the look &#8216;n&#8217; feel of the site is consistent, while keeping the views DRY. I&#8217;ll also be working with Loren Johnson to make Radiant available as a plugin which will make integrating Radiant with Rails even easier.</p> Thu, 31 May 2007 14:11:00 -0500 urn:uuid:e29bef12-3824-45ba-8294-f472d8bd9acc http://blog.pearware.org/2007/05/31/radiantonrails-released-on-rubyforge#comments web ruby rails radiant radiantonrails rails extension realidaho http://blog.pearware.org/2007/05/31/radiantonrails-released-on-rubyforge Switching from Pound to Nginx <p>I just switched some <a href="http://www.rubyonrails.com">Ruby on Rails</a> apps I&#8217;m running from <a href="http://www.apsis.ch/pound/">Pound</a> to <a href="http://sysoev.ru/nginx/">Nginx</a> based on the results from some <a href="http://blog.kovyrin.net/2006/08/28/ruby-performance-results/">articles</a> I&#8217;ve read online. The two biggest advantages of Nginx are 1. It&#8217;s raw performance, and 2. It can serve up static files, which is great for running <a href="http://wiki.rubyonrails.org/rails/pages/Capistrano">Capistrano&#8217;s</a> <strong>disable_web</strong> command to show a maintenance page when redeploying an application.</p> <p>At work, we&#8217;re working on a standard Ruby on Rails setup and are currently investigating two options. The first is a <a href="http://mongrel.rubyforge.org/">Mongrel cluster</a> running behind Nginx, as I described above. The second option is fronting the Mongrel cluster with <a href="http://www.lighttpd.net/">Lighttpd</a>. Since the 1.4.x series of Lighttpd is <a href="http://weblog.rubyonrails.org/2006/7/3/pound-makes-lighty-and-mongrel-play-nice">known</a> to have some issues with it&#8217;s mod_proxy implementation, we would use <a href="">Pen</a> until a stable 1.5 version is released.</p> <p>I&#8217;ll post another article once we have finished our evaluation and chosen which option we&#8217;re going to deploy at work. Stay tuned&#8230;</p> Tue, 05 Dec 2006 14:08:00 -0600 urn:uuid:5bc57c3a-1ceb-4323-aa46-63489c4abfe1 http://blog.pearware.org/2006/12/05/switching-from-pound-to-nginx#comments web ruby rails nginx capistrano rails ruby proxy load balancer http://blog.pearware.org/2006/12/05/switching-from-pound-to-nginx Switched to pure ruby ldap library <p>I <a href="http://blog.pearware.org/2005/5/23/ldap-authentication-in-rails">wrote an article awhile back</a> about using the Ruby/LDAP library to handle <span class="caps">LDAP</span> authentication in Ruby on Rails. I just finished swapping out the <span class="caps">LDAP</span> client library in that application from <a href="http://ruby-ldap.sourceforge.net/">Ruby/LDAP</a> to <a href="http://rubyforge.org/projects/net-ldap/">ruby-net-ldap</a>. The problems with Ruby/LDAP are that it isn&#8217;t a <span class="caps">GEM</span>, so installation is a bit more difficult, and it relies on a common <span class="caps">LDAP</span> library, like OpenLDAP, to already be installed on the system. The ruby-net-ldap library is written in pure Ruby, so no other library needs to be installed on the system.</p> <p>Here is the new code that performs the authentication:</p> <div class="CodeRay"><pre>require "net/ldap" class User < ActiveRecord::Base def self.authenticate(login, password, host, port) if login.to_s.length > 0 and password.to_s.length > 0 ldap = Net::LDAP.new ldap.host = host ldap.port = port ldap.auth = "cn=#{login},cn=users,o=xyz...", password if ldap.bin return find(:first, :conditions => ['username=?', login]) else return false end end end end</pre></div> Mon, 20 Nov 2006 13:58:00 -0600 urn:uuid:2a0d0e66-c586-41cc-bde8-f30b0a4dc58a http://blog.pearware.org/2006/11/20/switched-to-pure-ruby-ldap-library#comments ruby rails ruby net ldap rails authentication http://blog.pearware.org/2006/11/20/switched-to-pure-ruby-ldap-library ActiveMailer email performance on site5 <p>The site I am working on, <a href="http://www.realidaho.com">RealIdaho.com</a>, is hosted by <a href="http://www.site5.com">site5</a>. There are some forms that, when filled out by the user, send confirmation emails to the user, to us internally, and to the realtor&#8217;s cellphone. We started getting some reports that the confirmation page wouldn&#8217;t come up, and users were resubmitting the form many times. I discovered a few things while investigating that may be useful to others.</p> <p>The first thing I noticed, is that the page would timeout after 15 seconds and just return a blank page. Not very friendly, and definitely explains why the users were unsure that the form was submitted properly. The site however continued to process the request, and eventually got all the emails out. I contacted the site5 support team and asked the 1) why is the page timing out after only 15 seconds, and 2) why was sending email so slow.</p> <p>Here&#8217;s the answer I got for item 1:</p> <div style="width: 90%; margin: auto; font-style: italic;"> &#8220;Changing the timeout would affect the global settings for apache. This results in a significant increase in the number of open connections that apache has and degrades server performance. I can ask about this but I don&#8217;t think it will be possible to increase the timeout.&#8221; </div> <p>While I understand their reasoning, 15 seconds seems awfully short. I believe the default for apache is 5 minutes. Hopefully, they&#8217;ll consider increasing that amount at least to 30-60 seconds. While I never want a page that takes more than 5 seconds, I&#8217;m sure we&#8217;ll have some pages that do take longer, especially ones that need to communicate with external systems.</p> <p>Regarding number 2, the support technician suggested using sendmail directly instead of sending email over <span class="caps">SMTP</span>, as that should be much faster. So, I went into <span class="caps">RAILS</span>_ROOT/config/environments/production.rb and added the following line:</p> <p>ActiveMailer::Base.delivery_method = :sendmail</p> <p>Here are the performance numbers:</p> <p>Previous method (SMTP):</p> <p>Sending Mail to user (25.22760)<br/> Sending Mail to internal (31.11207)<br/> Sending Mail notification (20.70688)<br/></p> <p>Current method (Sendmail):</p> <p>Sending Mail to user (0.16841)<br/> Sending Mail to internal (0.28479)<br/> Sending Mail notification (0.24701)<br/></p> <p>Wow, that&#8217;s quite a difference! When I tested the form submission, the confirmation page appeared almost immediately. Now that&#8217;s more like it. Now I just need to see about increasing the timeout.</p> Thu, 09 Nov 2006 13:52:00 -0600 urn:uuid:8ece63ce-6a03-48d3-8e90-5b32d72749a0 http://blog.pearware.org/2006/11/09/activemailer-email-performance-on-site5#comments ruby rails rails ActiveMailer sendmail smtp site5 performance http://blog.pearware.org/2006/11/09/activemailer-email-performance-on-site5 Error working with large YAML files <p>As part of my <a href="http://blog.pearware.org/articles/2006/08/13/rails-migrations-dropping-default-constraint-on-column">Application migration project</a>, I need to pre-populate the new database with zip code data. The <a href="http://pragmaticprogrammer.com/titles/fr_rr/">Rails Recipes book</a> (very useful) has a nice recipe on extracting fixtures from live data. So, I&#8217;ve created a zips.yml file that contains all the zip code data that I can insert into the new database. However, when I try to load the fixture using this <a href="http://rails.techno-weenie.net/tip/2006/6/8/bootstrapping_your_database">very cool rake task from Technoweenie</a>, the <span class="caps">YAML</span> library throws the following exception: <strong>SystemStackError: stack level too deep</strong>.</p> <p>It is possible to work-around the error by increasing the stack limit on the command-line. on Mac <span class="caps">OSX</span> (and probably Linux/Unix), the following command can be run before running the rake task: <strong>ulimit -s 32768</strong>. This increases the default stack limit to 32MB, which should be enough, unless the yaml file is really large, I suppose.</p> <p>Does anyone know if somebody is working on fixing the <span class="caps">YAML</span> library to be nicer to the stack?</p> Sun, 13 Aug 2006 13:34:00 -0500 urn:uuid:5fdcea08-eeac-48bf-976c-5b5c89f34597 http://blog.pearware.org/2006/08/13/error-working-with-large-yaml-files#comments ruby rails yaml error bootstrap rake http://blog.pearware.org/2006/08/13/error-working-with-large-yaml-files Rails Migrations - Dropping default constraint on column <p>I&#8217;m working on a project for a client to convert a website from <a href="http://www.php.org"><span class="caps">PHP</span></a> to <a href="http://www.rubyonrails.com">Ruby on Rails</a>. The database is also changing from Microsoft <span class="caps">SQL</span> Server to MySQL, along with several schema redesigns.</p> <p>One thing I wanted to do to our new db, was change a foreign key column from <strong><span class="caps">NOT NULL DEFAULT 0</span></strong> to allow nulls and remove the default. Currently, the organizations table has a record with ID = 0 to signify a blank organization. Users that do not belong to an organization, have a foreign key to that Zero-record. Well, a better way to represent that is for the foreign key column in users to simply be <span class="caps">NULL</span> and ditch the Zero-record in the organizations table.</p></p> <p>I have created a Migration class to handle the schema change. However, it looks like I have to drop to <span class="caps">SQL</span> as there is not Migration way of doing this. Here&#8217;s what it looks like:</p> <pre><code> execute 'ALTER TABLE users ALTER organization_id DROP DEFAULT'</code></pre> <p>I wanted to be able to handle this in the Migration-syntax, but I don&#8217;t think that&#8217;s possible. It would be nice to do something like:</p> <pre><code> change_column :users, :organization_id, :integer, :default => :null, :null => true</code></pre> Sun, 13 Aug 2006 13:31:00 -0500 urn:uuid:9de7ba89-c5a0-418e-9b5c-31b19a33b1f0 http://blog.pearware.org/2006/08/13/rails-migrations-dropping-default-constraint-on-column#comments ruby rails database migrations schema mysql http://blog.pearware.org/2006/08/13/rails-migrations-dropping-default-constraint-on-column Rails redeploy issue resolved <p>I just redeployed a rails site for a client of mine, <a href="http://www.realidaho.com">Real Idaho</a>, using <a href="http://manuals.rubyonrails.com/read/book/17">Capistrano</a> and it didn&#8217;t work properly. I was able to resolve the issue, and here are the details in case this ever happens to you&#8230;</p> <p>Running</p> <pre><code>$ rake deploy</code></pre> <p>gave the following output (some content suppressed):</p> <pre><code>... transaction: commit * executing task restart * executing "/home/*****/apps/*****/current/script/process/reaper -d 'dispatch.fcgi'" servers: ["216.118.83.207"] [216.118.83.207] executing command ** [out :: 216.118.83.207] bash: /home/*****/apps/*****/current/script/process/reaper: Permission denied command finished rake aborted! command "/home/******/apps/******/current/script/process/reaper -d 'dispatch.fcgi'" failed on 216.***.***.***</code></pre> <p>The problem is that the files in script/ and the dispatch.* files in public need to be executable, but when <a href="http://manuals.rubyonrails.com/read/book/17">Capistrano</a> pulls down the latest subversion code, it defaults those files to the permissions:</p> <pre><code>-rw-r--r--</code></pre> <p>which is just read/write.</p> <p>In order to have subversion pull down the files with the correct, executable, permissions, you must run the following command on each executable file and commit them to subversion:</p> <pre><code>$ svn propset svn:executable</code></pre> <p>I found the following article, <a href="http://blog.teksol.info/articles/2006/03/09/subversion-primer-for-rails-projects">Subversion Primer for Rails projects</a>, which has a nice script to automate the task of setting the necessary files as executable. Here&#8217;s the script:</p> <pre><code>$ svn propset svn:executable "*" `find script -type f | grep -v '.svn'` public/dispatch.*</code></pre> <p>Then, run</p> <pre><code>$ svn commit</code></pre> <p>to commit those changes to Subversion.</p> <p>I think this should resolve the issue, but I haven&#8217;t redeployed yet, so I haven&#8217;t verified that this will really fix my deployment issue.</p> Sun, 06 Aug 2006 11:25:00 -0500 urn:uuid:6c7d5d72-611c-4f06-b09b-70496d0c9c99 http://blog.pearware.org/2006/08/06/rails-redeploy-issue-resolved#comments web ruby rails capistrano rails deploy errors http://blog.pearware.org/2006/08/06/rails-redeploy-issue-resolved