RailsConf Europe 2007: Day Three

The third and last day of the RailsConf Europe started with a wonderfully well presented keynote by Cyndi Mitchell of ThoughtWorks, called Bring Ruby to the Enterprise, Not the Other Way 'Round, of which I unfortunately can't find an online version.

The second keynote of the day was about Best Practices, by Marcel Molina and Michael Koziarski, which gave some interesting insights, such as using explicit (and long) names for actions and having small controllers (not more of 5 actions per controller and 5 lines per action), you should use the models for that.

After the break, I assisted the Building Rich Internet Applications with Flex and Ruby on Rails by Simeon Bateman, where he presented the possibilities of using Flex to create rich internet applications, supported by Mac, Windows, and (soon) Linux.


Building applications with Flex

AMV2 open sourced (Tamarin project in Mozilla)

HTTPService -> connect RESTful interfaces in Rails

WebORB -> plugin for Rails (themidnightcoders.com)                                           

RubyAMF

flex.org/ruby


The next session, Ruby on Rails Security, by Heiko Webers, while a bit basic at the beginning, gave some interesting insights as well. Some unedited notes follow:


"An insecure server is like a tunnel into Fort Knox"

Apache:
    - unprivileged user
    - deactivate modules
    - uploads out of DocumentRoot
    - disallow access, allow in particular

MySQL:
    - unpriv. user
    - bind localhost whenever possible
    - independent users for databases
   
Profiling:
    - comments!
    - leftover files
    - debug actions
    - robots.txt
    - Google Hacking Database ???

Interpreter Injection
    - OWASP Top Ten
    - User Agent Injection
        - XSS, Browser Injection
        - xssed.com
       
Cookies
    - session_id after auth
    - stolen!
    - sniffing, read document.cookie using code injection

UA Injection Countermeasures
    - Markdown (for mark-up)
    - RedCloth (some injection still possible)
    - Full HTML
        - blacklist filter
        - whitelist filter: WhiteListHelper plugin
    - No HTML at all
        - no strip_tags (you can go around and still inject some code)
        - use sanitize, SafeERB plugin

SQL Injection
    - Unauthorized reading (without using [] in finds)
    - Needs ', " or nil + line break
    - Conditions hash
   
Interpreter Injection
    - ActiveForm plugin: i.e. validates_length_of, validates_format_of for regexp
    - Mass Assignment (modify form names when using arrays)
        - Assign individually, use attr_accessible

Ajax
    - Output filters, sanitize!


Finally, I assisted to the MindMeister Development Study Case by Michael Hollauf. MindMeister is a collaborative mind map web application, wonderfully designed and with a lot of potential. He presented us with mostly the business part of the application, usability, and several other aspects of marketing a start-up.

We were also informed that the RailsConf Europe 2008 will be in Berlin as well. All I can say is I expect to be there next year (and maybe Portland, too), and that I'll be writing a post recapping my impressions on this year's RailsConf Europe, which has been very interesting, and of course, a lot of fun.



 

RailsConf Europe 2007: Day Two

Today was the big day, or the day the "real" RailsConf started (yesterday we only had tutorials). It also featured a DHH keynote at about 9:15am.

David's keynote was really interesting, and he focused on saying that while on the previous RailsConfs he had been announcing the Rails birthdays, and how many books about Rails were being published, or as he said, "how freaking awesome we are", this was the RailsConf where Rails ended being a surprisingly backed framework to convert itself into a serious competitor to other similar frameworks. He paraphrased Gandhi when he said: "first they ignore, then they laugh at you, then they fight you, and then you win."... and then what? It all happened so fast.

He also talked about the new features that Rails 2.0 will have, such as improved migrations, better routes, and different layouts depending on devices made easy (Hello, iPhone), and he also announced that a preview of Rails 2.0 will (probably) be launched before the ending of the RailsConf.

The next conference, Caching in a Multilanguage Environment, by Benjamin Krause was interesting as well, giving some ideas on how to use the client headers to show them the site in different languages, as well as different formats (HTML, XML), while still being able to cache data. Some notes (to be edited) from this conference follow:

format negotiation -> respond_to

map.named_route -> use Accept-Language Header

Use before_filter to get language on App controller
    - Gettext / globalize
   
Reimplement cache_page method, add request.language (accepts content but not language)

Use mod_rewrite, to make a subrequest to apache, getting correct path.

Fragment Caching

<% cache do %> -> MemCache

Several MemCache, send language fragments to corresponding MemCache (or one MC w/ namespaces)

plugins: mlr, mlcache -> in svn.omdb.org


The next conference, by Dr. Nic was really really funny, as usual, giving some interesting ideas about his "magic". He's uploaded the slides to his blog: Meta-Magic in Rails: Become a Master Magician



 

RailsConf Europe 2007: Day One (Tutorials)

After yesterday's Bratwurst on Rails, where we met some of the people that would be attending the conference while having a bratwurst and some drinks at the Kalkscheune, the next event was the Tutorials Day, starting at about 8:30am.

The early morning walk from the Radisson SAS hotel where I'm staying to the Maritim proArte where the conference was held was pleasant, consisting of about 1km down Unter den Linden to Friedrichstrasse.

Unfortunately, the Handling Large Concurrent Uploads tutorial was cancelled, so I switched to A Half-day of Behavior-driven Development on Rails, by David Chelimsky, Dan North and Aslak Hellesøy, which turned to be quite interesting, and quite enjoyable as well. These are some of the notes that I took from the tutorial (to be edited):

Behaviour Driven Development
writing software that matters

link what software should do with tech

test naming should be linked to what it's testing

agilemanifesto.org

rspec tool for tdd

agiledocs

BDD:   
    Narrative
        As a [role]
        I want [feature]
        So that [benefit]

    Acceptance criteria ->
        Scenarios:
            Given [some context]
            Given [some other context]
            When [some event occurs]
            Then [expect some outcome]
            And [another outcome]
           
rcov -> Coverage tests

rake stories          


At the afternoon, I assisted to the Scaling a Rails Application from the Bottom Up in Europe, by Jason Hoffman (Joyent), quite similar to the one that he had already presented in the RailsConf in Portland, and that I had already read. He gave some interesting ideas as well.



Here are some notes of the tutorial (to be edited as well):

tiers should be 10x different in throughput
not spending more than 10% of revenue

he likes ruby as process
should not be threaded

glassfish -> generating autodeploying war files
             better performance ( mongrel 150-400 vs 1000 req/sec), more memory footprint

use dns to scale (powerdns)
    - group users by hostname (jason.strongspace.com)
    - use GEO database to send them to different datacenters

layer7
    - separate mongrels for different controllers
    - can even give priority to first time users (new ip)
    - can give priority to certain controllers, scale controllers, bring down controllers one by one

nginx, lighty -> <1000 req/sec

varnish: web service command line allows to "purge" cache to update at next hit
         10000 req/sec

Application separation
- dynamic

- static

- uploads:
    - separate from app

- downloads:
    - mod_secdownload in lighty

tsung -> benchmark tool




 

Attending RailsConf Europe 2007

RailsConf Europe Logo I have recently registered and will be attending the RailsConf Europe 2007 conference held in Berlin, from September 17th to the 19th. You can take a look at the schedule of the conference here: RailsConf Europe 2007 Schedule. There's even a Bratwurst on Rails event on the 16th you can register for free.

See you in Berlin!



 

Deploy Ruby on Rails applications using Rake

So you are a Rails developer (or just starting to test Rails), and you would like to find an easy way to deploy a new version of your web application to your production machine, just uploading the files you have changed, and not the entire project.

Rake is Ruby's version of Make, and uses Rakefiles (clever, huh?). You can use Ruby to make your own Makefiles. We can then use Rake to deploy our application.

You will need the rsync client (you don't need the server) installed in your destination server. Otherwise, the process will fail.

Create a new file, named deploy.rake in lib/tasks/, containing the following code:

task :deploy => :environment do
  puts "Deploying to server"
  system("rsync -rltvz -e ssh app public root@server:/var/www/webapp/")
  system("ssh root@server chown -R www-data:www-data /var/www/webapp/")
  system("ssh root@server /etc/init.d/apache2 reload")
end


This will sync both app/ and public/ directories (the application itself, images and stylesheets), but you can add as much directories as you need. The second line changes the permissions to the user that runs the web server, and the third, as you may see, reloads Apache to force the server to refresh the controllers.

And what if you have several servers where to deploy? We can use some Ruby style loops:

task :deploy => :environment do
  servers = %w{server1.domain server2.domain server3.domain}
  servers.each do |server|
    puts "Deploying app to #{server}"
    system("rsync -rltvz -e ssh app public root@#{server}:/var/www/webapp/")
    system("ssh root@#{server} chown -R www-data:www-data /var/www/webapp/")
    system("ssh root@server /etc/init.d/apache2 reload")
  end
end


Now, in your main application directory, just run rake deploy and the remote host will be synced with your local application.

Still, if you don't have already enabled key authentication for your ssh account, you will be asked for a password each time you deploy your application.

If you want to do this process without having to enter your password, you can generate your own authentication key:

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/daniel/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/daniel/.ssh/id_rsa.
Your public key has been saved in /home/daniel/.ssh/id_rsa.pub.
The key fingerprint is:
e3:c6:4d:88:c0:75:7b:2a:65:52:91:19:b4:99:e1:8f daniel@client

If you don't specify a password, you won't need to enter it to unlock the private key. It is safe to leave it blank, but don't let anyone get to your id_rsa private key, or they will be able to log in to your servers.

The public key is contained in .ssh/id_rsa.pub. This is the key you have to upload to your servers.

$ scp .ssh/id_rsa.pub root@server:
root@server's password:
id_rsa.pub                                                           100%  233     0.2KB/s   00:00   

Log in now to your server as the user you want to have authorized, and copy the public key to the authorized_keys file:
# cat id_rsa.pub >> .ssh/authorized_keys



 

Benchmarking Rails enabled Web Servers

Following the article Running Rails applications: Choosing a server where I compared the different combinations server/dispatcher available for Rails, I have done a small benchmark to see what server actually performs better.

The servers I have tested are: Apache2 + Dynamic mod_fastcgi, Apache2 + mod_fcgid, Apache + Static mod_fastcgi and Lighttpd 1.4 + mod_fastcgi. The benchmark was performed using ab (Apache Benchmark), in a machine in the same LAN as the server (an old Pentium-4 with 512Mb of RAM), running first 100 requests with a concurrency level of 10 (ab -c 10 -n 100), and then, 500 requests with a concurrency level of 50 (ab -c 50 -n 100).


Configuration used:

Apache2 + Dynamic mod_fastcgi:

<IfModule mod_fastcgi.c>
AddHandler fastcgi-script .fcgi
FastCgiConfig -minProcesses 2 -maxProcesses 4
</IfModule>

Apache2 + mod_fcgid:

<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi
SocketPath /var/lib/apache2/fcgid/sock
IPCConnectTimeout 20
</IfModule>

Apache2 + Static mod_fastcgi

<IfModule mod_fastcgi.c>
AddHandler fastcgi-script .fcgi
FastCgiServer /path/to/dispatch.fcgi -processes 4
</IfModule>

Lighttpd + mod_fastcgi

server.modules   += ( "mod_rewrite", "mod_redirect", "mod_fastcgi" )

fastcgi.server = (
#Start a FastCGI server for Rails
".fcgi" =>
( "rails" =>
(
"bin-path" => "/path/to/dispatch.fcgi",
"min-procs" => 2,
"max-procs" => 4,
"socket" => "/tmp/rails.fastcgi"
)
)
)


Apache 2.2 + Dynamic mod_fastcgi (min-processes 2)


100 requests; Concurrency level: 10

Document Length:        4558 bytes

Concurrency Level: 10
Time taken for tests: 13.951477 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 488300 bytes
HTML transferred: 455800 bytes
Requests per second: 7.17 [#/sec] (mean)
Time per request: 1395.148 [ms] (mean)
Time per request: 139.515 [ms] (mean, across all concurrent requests)
Transfer rate: 34.12 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 2.1 0 10
Processing: 1139 1342 425.6 1182 3056
Waiting: 1139 1342 425.1 1181 3053
Total: 1139 1343 427.4 1182 3056

Percentage of the requests served within a certain time (ms)
50% 1182
66% 1262
75% 1281
80% 1286
90% 2006
95% 2683
98% 2962
99% 3056
100% 3056 (longest request)



500 requests; Concurrency level: 50

Document Length:        4558 bytes

Concurrency Level: 50
Time taken for tests: 73.904226 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 2441500 bytes
HTML transferred: 2279000 bytes
Requests per second: 6.77 [#/sec] (mean)
Time per request: 7390.423 [ms] (mean)
Time per request: 147.808 [ms] (mean, across all concurrent requests)
Transfer rate: 32.26 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.6 0 2
Processing: 121 7142 4947.1 5644 25456
Waiting: 121 7142 4947.1 5644 25455
Total: 123 7142 4947.3 5644 25457

Percentage of the requests served within a certain time (ms)
50% 5644
66% 6027
75% 6468
80% 6615
90% 19164
95% 21813
98% 22193
99% 22493
100% 25457 (longest request)


Apache 2.2 + mod_fcgid 


100 requests; Concurrency level: 10

Document Length:        4558 bytes

Concurrency Level: 10
Time taken for tests: 11.987351 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 488800 bytes
HTML transferred: 455800 bytes
Requests per second: 8.34 [#/sec] (mean)
Time per request: 1198.735 [ms] (mean)
Time per request: 119.874 [ms] (mean, across all concurrent requests)
Transfer rate: 39.79 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 36 119.8 0 493
Processing: 179 1124 603.8 1048 3094
Waiting: 0 1115 610.1 1048 3094
Total: 198 1161 622.0 1048 3094

Percentage of the requests served within a certain time (ms)
50% 1048
66% 1280
75% 1652
80% 1731
90% 2082
95% 2506
98% 2663
99% 3094
100% 3094 (longest request)


500 requests; Concurrency level: 50

Document Length:        4558 bytes

Concurrency Level: 50
Time taken for tests: 77.213299 seconds
Complete requests: 500
Failed requests: 18
(Connect: 0, Length: 18, Exceptions: 0)
Write errors: 0
Non-2xx responses: 18
Total transferred: 2361938 bytes
HTML transferred: 2198180 bytes
Requests per second: 6.48 [#/sec] (mean)
Time per request: 7721.330 [ms] (mean)
Time per request: 154.427 [ms] (mean, across all concurrent requests)
Transfer rate: 29.87 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 98 317.2 0 1203
Processing: 140 6214 15936.0 950 76018
Waiting: 133 6210 15935.9 946 76017
Total: 140 6313 16175.8 973 77211

Percentage of the requests served within a certain time (ms)
50% 973
66% 1377
75% 1827
80% 1985
90% 20811
95% 61097
98% 63166
99% 70184
100% 77211 (longest request)


Apache 2.2 + Static mod_fastcgi (processes 4)


100 requests; Concurrency level: 10

Document Length:        4558 bytes

Concurrency Level: 10
Time taken for tests: 10.954809 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Non-2xx responses: 100
Total transferred: 488300 bytes
HTML transferred: 455800 bytes
Requests per second: 9.13 [#/sec] (mean)
Time per request: 1095.481 [ms] (mean)
Time per request: 109.548 [ms] (mean, across all concurrent requests)
Transfer rate: 39.02 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 250 293.3 86 996
Processing: 85 809 845.6 679 6093
Waiting: 0 734 857.3 615 6092
Total: 422 1059 837.3 919 6093

Percentage of the requests served within a certain time (ms)
50% 919
66% 1039
75% 1039
80% 1081
90% 1172
95% 2755
98% 5405
99% 6093
100% 6093 (longest request)


500 requests; Concurrency level: 50

Document Length:        4558 bytes

Concurrency Level: 50
Time taken for tests: 60.694702 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 2441500 bytes
HTML transferred: 2279000 bytes
Requests per second: 8.24 [#/sec] (mean)
Time per request: 6069.470 [ms] (mean)
Time per request: 121.389 [ms] (mean, across all concurrent requests)
Transfer rate: 39.28 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.6 0 2
Processing: 240 5789 1069.7 5993 7837
Waiting: 240 5789 1069.8 5993 7837
Total: 242 5789 1069.3 5993 7837

Percentage of the requests served within a certain time (ms)
50% 5993
66% 6129
75% 6231
80% 6277
90% 6400
95% 6508
98% 6678
99% 6972
100% 7837 (longest request)


Lighttpd 1.4 + mod_fastcgi (min-procs 2; max-procs 4)

100 requests; Concurrency level: 10

Server Software:        lighttpd/1.4.13
Document Length: 4558 bytes

Concurrency Level: 10
Time taken for tests: 11.976793 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 481100 bytes
HTML transferred: 455800 bytes
Requests per second: 8.35 [#/sec] (mean)
Time per request: 1197.679 [ms] (mean)
Time per request: 119.768 [ms] (mean, across all concurrent requests)
Transfer rate: 39.16 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 183 1160 657.1 1020 2889
Waiting: 174 1040 670.8 874 2735
Total: 183 1160 657.1 1020 2889

Percentage of the requests served within a certain time (ms)
50% 1020
66% 1234
75% 1658
80% 1830
90% 2108
95% 2561
98% 2833
99% 2889
100% 2889 (longest request)


500 requests; Concurrency level: 50


Server Software:        lighttpd/1.4.13
Document Length: 4558 bytes

Concurrency Level: 50
Time taken for tests: 62.138366 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 2405500 bytes
HTML transferred: 2279000 bytes
Requests per second: 8.05 [#/sec] (mean)
Time per request: 6213.837 [ms] (mean)
Time per request: 124.277 [ms] (mean, across all concurrent requests)
Transfer rate: 37.80 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 170 547.2 0 2068
Processing: 105 5755 1372.4 6227 6909
Waiting: 5 5686 1383.7 6201 6682
Total: 2074 5926 955.5 6228 6928

Percentage of the requests served within a certain time (ms)
50% 6228
66% 6285
75% 6325
80% 6411
90% 6468
95% 6485
98% 6528
99% 6542
100% 6928 (longest request)

Conclusion

As we may already be expecting, the worst performer is Apache2 + Dynamic mod_fastcgi.

Apache2 + mod_fcgid
is performing surprisingly bad with a lot of stress. However, the performance could probably be better changing some of the configuration parameters.

Either Apache 2 + Static mod_fastcgi or Lighttpd + mod_fastcgi is the way to go, the latter performing a little better. My recommendation: use Lighttpd whenever possible. If you need to use Apache2, set mod_fastcgi in static mode.




 

Forwarding downloads from the dispatcher to the web server

If you have a Rails application which requires for the users to be able to download files, either because you authenticate them, or because you need some pre-processing before serving the file, you will probably use send_file or send_data to send them from your action.

This works quite well when the files are small and/or you do not have too many concurrent requests. If the latter happens, you will end up using too many resources (i.e. too many dispatchers), thus using more RAM, and eventually, swapping too much.

The solution I'm bringing to you today, is something I have recently started using in in.solit.us. Basically, this method will use the X-Sendfile header to forward the download requests to your web server (either Lighty or Apache2).

This feature is already supported by default in Lighty. You may want to take a look at the following article in Lighttpd's blog: mod_proxy_core got X-Sendfile Support.

Setting this up in Apache2 is not so easy, and requires using mod_xsendfile, written by Nils Maier, licensed under the Apache License.

First of all, we need to compile mod_xsendfile for our Apache installation.

Download the module from mod_xsendfile page:
persefone:~# wget http://celebnamer.celebworld.ws/stuff/mod_xsendfile/mod_xsendfile.c

You will need apxs2 for this, so if executing apxs2 gives you a command not found, you will need to install the Apache development headers.

If you are using the default Apache2-MPM-Prefork issue the following command:
persefone:~# apt-get install apache2-prefork-dev

Otherwise (MPM-Worker) install the threaded development headers,
persefone:~# apt-get install apache2-threaded-dev

At this point, you can compile the module using the following command. A line in httpd.conf enabling the module will be automatically added.
persefone:~# apxs2 -cia mod_xsendfile.c
...
[activating module `xsendfile' in /etc/apache2/httpd.conf]


You need to activate now the module for the directory you want to use it. You can do it either in /etc/apache2/sites-enabled/ or in an .htaccess file in the directory.

If we wanted to enable /var/www/downloadable_files/, we will need the following:
<Directory /var/www/downloadable_files>
   
...
    XSendFile on
   
XSendFileAllowAbove on (If set to ON, the module will allow to send files above the specified directory).
</Directory>

Now, the Rails goodies:

#Check if file exists and maybe authenticate?
if File::exist? "#{@path}/#{@archive.url}"
    #send_file("#{@path}/#{@archive.url}",
    #                  :filename => @archive.original_name)  

    #The code below equals the send_file function above.
    response.headers['Content-Type'] = "application/force-download"
    response.headers['Content-Disposition'] = "attachment; filename=\"#{@archive.original_name}\""
    response.headers["X-Sendfile"] = "#{@path}/#{@archive.url}"
    response.headers['Content-length'] = @archive.length
    render :nothing => true
      
end

The code explains for itself. In Content-Disposition we will set under filename the name we want the file to appear to the user who downloads it, and we have to supply the X-Sendfile header with the path+name of the file.

The last command (render :nothing => true) tells Rails not to render anything. If Rails renders something, Apache2 will not get the X-Sendfile request.



 

Using RMagick/ImageMagick in Rails to resize images

ImageMagick is, as you may already know, a software suite used to read, create and convert images. At this point, you may have already noticed the potential of ImageMagick in combination with Rails to read, resize, rotate, generate captchas... you name it.

The connection between ImageMagick and Rails is done using a gem called RMagick. You can take a look at the User's Guide/API here: RMagick User's Guide. As many of the other Rails developers around, I use a Mac to develop my applications, and then I deploy them in a Linux server. As the Mac installation is perhaps a bit trickier, I'm describing it too.

Installing ImageMagick/RMagick in MacOS X

If you don't have imagemagick already installed, the easiest way to do so is using MacPorts (formerly DarwinPorts). You can follow the instructions on the MacPorts site: Installing MacPorts.
Once you have MacPorts installed, you can easily install ImageMagick issuing the following command in your Terminal (you will be asked for your password):

macbookpro:~ daniel$ sudo port install imagemagick

Now, I'm supposing you already have RubyGems installed.
If not, download the source from RubyForge, compile it with ./configure; make and install it with make install.

So, we can proceed to install the RMagick gem. The tricky part is that you need to set some environment variables to point to the place where the ImageMagick binaries are installed. Open your Terminal, and set the environment variables copying and pasting the following lines:

export MAGICK_HOME=/usr/local/ImageMagick/
export DYLD_LIBRARY_PATH="$MAGICK_HOME/lib/"
export PATH="$MAGICK_HOME/bin:$PATH"


You can now issue the gem install rmagick command which will download, compile and install the RMagick gem automatically.

Installing ImageMagick/RMagick in Linux

The installation under Linux is much easier than in MacOS X. Install ImageMagick and development libraries first and then install the gem (and rubygems too, if you don't have it - apt-get install rubygems -).

newton:~# apt-get install imagemagick libmagick9-dev
newton:~# gem install RMagick

Once you have installed RMagick, using it in your Rails application is as easy as adding the line require 'RMagick' in the controller you need RMagick.

In the following example, we create the preview action in our controller, which will, in a simple way, resize any image to a maximum of 200x200.

def preview
  @archive = Archive.find(params[:id])
   if @archive.contenttype == "image"         
      img_orig = Magick::Image.read("/my/picture/path/"+@archive.filename).first
      img = img_orig.resize_to_fit(200,200)
     @response.headers["Content-type"] = img.mime_type
      render :text => img.to_blob   
   end
end

We could use now this action we created to access the image from another view:
    <img src="/archives/preview/<%= @archive.id -%>">

You can take a look at the API to see the complete list of transformations you can do with RMagick: RMagick User's Guide



 

Improving performance in Rails with Apache2

The standard Apache2 installation shipping with most Linux distributions uses MPM prefork. While this is a good model, for instance, when using apache modules to run PHP, it does not perform that well when running Rails using FastCGI. Instead, we can use MPM Worker for a better performance.

So, what's the difference between prefork and worker?

Quoting from the Apache MPM Prefork page:
MPM Prefork implements a non-threaded, pre-forking web server that handles requests in a manner similar to Apache 1.3.

And for the Apache MPM Worker says:
MPM Worker implements a hybrid multi-process multi-threaded server. By using threads to serve requests, it is able to serve a large number of requests with less system resources than a process-based server.

So, you have decided you want to switch to Apache2 MPM Worker. One of the problems you may find when doing an apt-get install apache2-mpm-worker is that dpkg will automatically delete the PHP module and other packages associated to it. The solution is to let dpkg remove PHP and install it by ourselves.

We will have to install some build-related packages if they were not installed before:
persefone:~# apt-get install build-essential flex libxml2-dev

Installing apache2-mpm worker:
persefone:~# apt-get install apache2-mpm-worker (will remove mpm-prefork, and libapache2-mod-php and related packages)

We need to install libmysqlclient development headers to compile PHP with mysql support:
persefone:~# apt-get install libmysqlclient15-dev

We also need apxs2, so we will install the apache2 development headers for mpm worker:
persefone:~# apt-get install apache2-threaded-dev

We can download the PHP source code from http://www.php.net/downloads.php and compile it as usual:
persefone:~# ./configure --with-apxs2=/usr/bin/apxs2 --with-mysql --enable-maintainer-zts --prefix=/usr
persefone:~# make
persefone:~# make install

There is no need to make a symbolic link at /etc/apache2/mods-enabled/ for php5 now, as it is automatically enabled.



 

Install Instiki in Apache using FastCGI

Instiki is a Wiki clone made in Ruby on Rails really easy to setup and use. However, the installation manuals on the Instiki page are based in using WEBrick and not Apache.

The first step, is, of course, to download the Instiki tarball package in http://rubyforge.org/projects/instiki/. The most recent version available for now is 0.11.

As usual, we untar the package, renaming the directory to instiki:
persefone:/var/www# tar xvfz instiki-0.11.0.tgz
persefone:/var/www# mv instiki-0.11.0 instiki

Set up the permissions for the apache user:
persefone:/var/www# chown www-data:www-data instiki/ -R

On instiki/public, we need to edit the first line of the dispatch.fcgi file, changing:
#!c:/ruby/bin/ruby
with:
#!/usr/bin/ruby (the most common place where the ruby binary is located)

We will also need to modify the .htaccess file in the same directory. You'll have to search for the line:
RewriteRule ^(.*)$ dispatch.cgi [QSA,L]

replacing it with:
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

If you use mod-fcgid instead of mod-fastcgi, you will have to change the following line too, in .htaccess:
AddHandler fastcgi-script .fcgi
replacing it with:
AddHandler fcgid-script .fcgi

We should define a MySQL database too (by default sqlite will be used), editing database.yml under instiki/config

production:
  adapter: mysql
  host: localhost
  database: instiki
  username: instiki
  password: myinstikipassword
  socket: /var/run/mysqld/mysqld.sock


We usually will set up a new hostname for the wiki
persefone:/etc/apache2/sites-available# vim wiki.example.com

<Directory /var/www/instiki/public>
    Allow from all
    AllowOverride all
</Directory>

<VirtualHost *>
    ServerName wiki.example.com
    DocumentRoot /var/www/instiki/public/
</VirtualHost>


And we enable it making a symbolic link from sites-available to sites-enabled

persefone:~# ln -s /etc/apache2/sites-available/wiki.example.com /etc/apache2/sites-enabled/

Under the Instiki directory, we will generate the initial database with the following command (RAILS_ENV=production tells rake to generate the production database) :
persefone:/var/www/instiki# RAILS_ENV=production rake migrate

We can now reload Apache2 and Instiki should be working.



 

Search



About

newton.gra2.com is a blog about technology, opinion and random thoughts written by Daniel Alvarez, a computer engineer currently living in Zurich, Switzerland.

Topics

News (20/0)
Manuals (24/0)
Security (7/0)
Music (3/0)
Weeklog (1/0)
Personal (34/0)
Photos (3/0)
Opinion (14/0)
Ruby on Rails (12/0)
Windows (5/0)

Blogroll

Pros i contres (Jordi)
Entrepa de fusta (Oriol)
Spaghetti Code (Isaac)
Made in net (Eric)
Nogare (Juan)
Blog de Isaac Jimenez
Web d'en Jaume Benet
Montcada Wireless (Fran)
Blog d'en Ricard Forniol
Angela Fabregues
in.solit.us

Libertad Digital
FOX News
The Wall Street Journal
The Washington Times
The Jerusalem Post

Michelle Malkin
Eurabian News
Nihil Obstat
Barcepundit
Expose the left
Davids Medienkritik
Johan Norberg
Ayaan Hirsi Ali

User Functions

:

:


Lost your password?

Latest posts

Stories

No new stories

Comments last 2 days

No new comments

Trackbacks last 2 days

No new trackback comments

Links last 2 weeks

No recent new links