Posts Tagged ‘ruby’

LSRC 2010 – Day 2

August 28th, 2010

Below are my notes from the second day at LSRC.

Faster Test Suites

Nick Gauthier gave an impressive talk on reducing the execution time of a test suite.  The original suite took over 15 minutes to run.  Using factory_girl, shoulda and paperclip, he strongly recommended empty DB testing (no fixtures, all tests setup and tear down their objects/records), and the use of “no side-effect” test blocks.

Switching to the use of fast_context the execution time was brought down to 5 minutes.  Using perf_tools he found that calls to ImageMagic (from Paperclip) were calling out to the shell, taking a lot of time.  So he mocked those calls and brought it down to 3 minutes.

Using Hydra to do multi-core (concurrent) testing he brought execution time down to 1 minute.

Hydra is easy to set-up (no sockets or daemons to configure).  Just add a Rake task and configuration yml and you’re ready to use it.

When you run Test::Unit the Rails environment is loaded 4 times!!  Cucumber loads the environment twice.  RSpec and Hydra load it just once.

Further tweaking EXT4 journal_data_writeback and atime (access time) on the file system he got testing down to just 50 seconds.  Using Ruby Enterprise Edition (with the tmalloc) and more tweaks (this time to the Ruby _HEAP and _GC settings) he had tests running in 18 seconds.  A 4417% increase in performance!!

Check the comments below for a link to Nick’s presentation slides.  He’s been kind enough to provide the link!

Searchability

Luigi Montanez gave an insightful presentation on SEO.  He doesn’t believe that you have to be an “expert” in SEO.  You simply need to have good content and Google (or Bing) will handle the rest.  There are some things you can do though to make sure you don’t sabotage your pages when crawlers visit them.

Make sure you have a sitemap.xml (sitemaps.org) and a robots.txt (robotstxt.org).  These help crawlers know what to pay attention to and what to ignore.  Make sure you use either a www or non-www domain.  If you have both choose one and redirect to it.  You don’t want to have the site indexed under both.

When redirecting make sure you use 302 and 301 appropriately.  302 is for a temporary redirect, and Google/Bing will not index the page it’s redirected to.  Use 301 instead, if your intention is to have the page permanently moved.  Be cautious too because many web frameworks will use a 302 by default, instead of a 301.

Do not change your content by region, nor require cookies for someone to view content.  Also page titles are very important.  Always follow the “Page Title | Site Title” style (or you can replace | with a – or some other delimiter, just be consistent).

In your URLs dashes (-) are word separators, underscores (_) are not!  Make sure you are using dashes in your URLs!!  Take advantage of meta tagging, and Google Webmaster Tools.

Takeaway:

  • Think like a searcher (search engine: crawler, index, rank)
  • Optimize your title tags (they are the most important thing)
  • Use Google Webmaster Tools (it provides valuable insight)

Components

Nick Sutterer gave a cool demonstration of Cells and Apotomo.  They allow you to create stateful widgets in your Rails views.

Padrino

Joshua Hull gave a talk on Padrino, another web framework built on top of Sinatra.  It takes a minimalist style approach (take what you need).  It’s light-weight and fast.  It has all the basic stuff you’d expect of  web framework including a simple mailer.

LSRC 2010 – Day 1

August 27th, 2010

Had a great info-filled day today at LSRC.  It’s my 3rd year attending.  Below are some of the highlights of the different talks I attended:

Real Software Engineering

Glenn Vanderburg gave a great opening talk about software engineering.  he said software engineering doesn’t work.  He explores the roots of the idea of “software engineering” all the back to the first NATO Software Engineering Conference on the subject.  At the conference they described best practices as:

  1. A software system is best designed if tests are interlaced with the software as it’s developed, rather than at the end.
  2. Creates a simulation which matches the requirements, contains the control which organizes the design of the system.
  3. Through successive repetitions of testing and design, the model ultimately becomes the software system itself.

Sound familiar?  He then talks about what went wrong, how did we start with what sounds like Agile and ended up with waterfall.  He talked about how the creator of waterfall process (Winston W. Royce) actually was trying to express he didn’t feel it worked.  His paper was so poorly written though that people (paying attention mostly to the fancy diagrams, and not the text) believed he was in favor of it.

Cost is *always* an object, and engineering is about what you can do with a dollar vs. what a “bumbler” can do with two.  Mathematical models are not supposed to be about correctness, but about saving on costs.  In traditional engineering you create models because it’s expensive to use up materials, labor, etc.  In software these things are extremely cheap and expendable, so mathematical models are less important.

Engineering is a science AND art.  It involves designing AND making, with cost AND elegance balanced.

Don’t build software as if it were bridges.  The source code *is* the design!  Customers are not paying for the source code.  It is the design of the software system.  Programs themselves are the models.  The customer in the end desires working software.

How to Build Awesome Teams

The guys from Hashrocket talked about their hiring process and what they felt made for great teams, core ideas:

  • Hiring
  • Transparency
  • Community
  • Methodology
  • Environment

They frown on:

  • Micro-management
  • Penny pinching (they still budget)
  • Death marches
  • Meetings (daily stand-up, monthly mission control)
  • Hierarchy
  • Future-proofing

When they hire candidates they look for people who are smart and get things done.  They look for individuals who like to communicate, participate and contribute back to the community.  They need to be a cultural fit.  They look at their online presence, see what they’re Twittering about, review their work on GitHub (code, commit hygiene, etc).  They also require a candidate to subject themselves to a week long interview, where they pair program and go to after-hours with the team.

Keynote: Tom Preston-Werner (GitHub)

Today everyone can start a business on their computer.  What you need though is an idea.

  • go to user groups (learn about people’s problems)
  • drink (seriously, it makes you honest)
  • find people you can work with (co-founders)
  • build something you love (that inspires you)
  • look for untapped potential (who are the early adopters, and how awesome can it become?)
  • keep your day job (not having time is a poor excuse)
  • fight entropy (keep it simple, and get stuff done)
  • ship it (or you might as well not exist) [ship it squirrel]
  • charge money (investors only care about money, not your company, so make customers your investors)
  • have fun! (“drink-ups” where drinking is sponsored)
  • never give it up (it’s going to be hard, don’t let fear hold you back)

If you want to have a $100,000 a year salary, you just need to get about 500 subscriptions at $12/mo (with a few premium or mid-level price options).  It’s totally realistic and in your grasp.  Don’t be afraid of not knowing something … people just like you started out with just an idea.

Fixing Rails 3 Beta ‘Invalid .gemspec format’ Errors

February 7th, 2010

If you’ve tried out Rails 3 on Ruby 1.9.1 (like myself) you might of noticed that a lot of errors are being sent to stderr, it’ll look something like this:

Ozawa Sakuro has posted a fix for this on GitHub.  You can see the original thread here, or simply follow the directions below to quickly fix it without trolling through the entire discussion:

1.  Edit lib/bundler.rb:

4: require 'bundler/rubygems-ext'  # originally 'bundler/rubygems'

2. Next rename lib/bundler/rubygems.rb to rubygems-ext.rb and then edit the file and wrap the require in a control structure as shown below:

1: unless defined? Gem
2:   require 'rubygems'
3:   require 'rubygems/specification'
4: end

I’ve tested this fix and it works with Bundler 0.9.3.  Version 0.9.4 resolves the issue (thanks for the update Christian).  In any event I hope this little nugget of information helps reduce the hair pulling.

Writing a Cucumber Feature

January 2nd, 2010

This is the second post about Cucumber, where we’ll be exploring the creation of a feature. Cucumber centers on testing the expected behavior of your application, rather than the expected behavior of a method. I had someone explain to me once, “unit tests/specs are the methods/implementation you wish you had, Cucumber tests are the application you wish you had.”

User Stories

When thinking about a Cucumber feature you will want to relate it to a user story. If you’re familiar with Agile development stories are nothing new.  For those who are not though it’s not too complicated. A story is simply an expression of value generally in the form of:  ”As {a role} I want/need {to do some function} so that {business value}.” Generally the business value is best expressed in terms of: revenue generation, revenue protection, operational efficiency (cost savings).

For the example we’ll be using this story:

As a workshop co-ordinator I need to manage contact information so I can easily reference individuals and start communications with them.

You’ll notice that this story doesn’t imply implementation.  You generally never want to imply implementation in a story.  Also you’ll notice that the story is simple and comprehensible by pretty much anyone (business user, developer, average Joe Public).

Starting a Feature File

In our Rails application we’ll want to go inside our /features folder.  Create a new file called manage_contact.feature.  At the top of the file type in the following:

Feature:  Manage Contact
  In order to easily reference individuals and start communications with them
  As a workshop co-ordinator
  I want to manage contact information

Now we have our first feature.  The next step is to define some scenarios.  Scenarios express various actions the user might take in relation to the feature.  Below are some of the scenarios we came up with for this feature (these go below your feature entry – indented to be even to the “In order, As a, I want” section).  You don’t need to, but I like listing the scenarios in order of priority, this way a developer working on the feature can just start on top and work downwards:

Scenario: View list of contacts

Scenario: Add a new contact

Scenario: Email a contact from the list

Scenario: Remove an existing contact

Scenario: Update an existing contact

Scenario: Start an IM conversation with a contact from the list

Your finished feature file should look something like this gist. In the next post we’ll start to look at building out our scenario’s with step definitions, that is define what behavior we expect to happen with each scenario we have defined.

Adding Cucumber to Your Salad

January 2nd, 2010

Cucumber has come a long way since I’ve first started to use it.  My two posts about getting started with BDD have been the most visited pages of my blog.  So it seems only fitting to address the new features and update the material.  I’ll be creating a series of blog posts (so it doesn’t end up being one giant scrolling page) addressing a real-life situation of creating a contact management feature.  We’ll build it from the ground up using Cucumber and RSpec.

Installing Cucumber / RSpec

The first step to using Cucumber (and RSpec) is to install the necessary gems.  This is very straightforward and probably familiar to most people:

sudo gem install rspec rspec-rails cucumber cucumber-rails webrat ZenTest database_cleaner

If you are using Ruby > 1.9 you will also need to install the test-unit gem (or RSpec will not load properly).

sudo gem install test-unit -v=1.2.3

Now if you run into errors installing webrat, know that it requires nokogiri (an awesome XML parser, see my blog post about nokogiri).  In order to build nokogiri though you need to have a few libraries installed ahead of time.  If you run into any errors installing webrat (likely because it couldn’t install the dependency nokogiri gem) try the following (I’m using a Debian -Kbuntu- flavor of Linux, so your specific command might vary):

sudo aptitude install libxml2-dev libxslt1-dev

Once you have installed the two developer libraries for libxml2 and libxslt you can try to reinstall webrat (sudo gem install webrat) and it should properly build.

Adding RSpec and Cucumber to a Rails Project

Having the gems installed is the first step.  To use Cucumber in your Rails project you need to run a few generation scripts which prime your application and make a few config updates. Run the following commands from inside your Rails application root:

script/generate rspec
script/generate cucumber

These scripts create new rake tasks, a spec and feature folder, as well as modify your database.yml (adding a new cucumber environment).  You’ll also notice a new cucumber environment file added to your environments folder already completed with all the necessary config.gem entries, nice.

That’s all there is to it for getting started.  Next post I will talk about outside-in development and starting your first feature!

Rails on Windows with JRuby

December 12th, 2009

Getting a working Rails stack on Windows can be a frustrating experience.  Rails was born in the Linux/Unix world so there are many libraries assumed to be on your system, but are not part of a regular Windows OS installation.  Below I share my own personal experience with getting up and running with Ruby and Rails using JRuby, which for me was the best way to go.

Requirements

Note the links above might move or change so you can visit (java.sun.comwww.jruby.org, www.postgres.com) and poke around to find them.

Installing Java and JRuby

This part is very easy.  Just double click the Java installer and follow the directions.  It will install Java to a location in your C:\Program Files (or the location you specify).  Do the same with JRuby.  I like to have JRuby live right under C:\jruby-1.4.0 so it is easy for me to get to (by default it will also go under C:\Program Files).

Now we need to edit our Windows environment and make sure that JRuby and Java can be seen on the path.  You’ll want to go to your Start > Control Panel > System > Advanced system settings and click the Environment Variables button.

Windows Environment VariablesYou might already see a JAVA_HOME value under User variables (and that’s ok).  You might even see one for JRUBY too.  I like to have the environment variables for Java and JRuby available at the system level however.

Click New under the System variables section and add entries for JDK_HOME and JRUBY_HOME as you see in the example on the right.  Click OK when you’re done.

Now open a command prompt window (Start > Accessories > Command prompt) and type in echo %JDK_HOME% and hit enter.  You should see the value you set, do this for (echo %JRUBY_HOME%) as well. Both should return the values you set previously in the Environment Variables window.  The last thing to do is add these values to your PATH.

Return to the Environment Variables window and edit the PATH value under system variables by selecting it and hitting Edit.  Entries are separated by a semi-colon (;) so make sure you add one to the front of the entries you’re adding unless there is one already there:

;%JDK_HOME%;%JRUBY_HOME%

Now after that’s done close and reopen your command prompt window (so that the new values are loaded).  Type echo %PATH% and you should see the two paths at the end of the results:

>> echo %PATH%
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
C:\Windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files(x86)\Git\cmd;
C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\bin

Now that all our paths seem in order we can further verify everything is working right:

>> java -version
java version "1.6.0_17"
Java(TM) SE Runtime Environment (build 1.6.0_17-b04)
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode)
>> jruby -v
jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02) (Java HotSpot(TM) 64-Bit)

Welcome to JRuby

At this stage you should have a working copy of JRuby.  JRuby is just like Ruby, except it uses Java and not the MRI/YARV interpreter.  Most of the commands you might be used to on a regular Ruby stack work the same in JRuby, except often you use jruby -S to run your commands rather than ruby.  Let’s start installing a few of my favorite gems:

jruby -S gem install rails haml nokogiri shoulda factory_girl cucumber webrat ^
rspec rspec-rails will_paginate ZenTest redgreen fastercsv RedCloth paperclip ^ 
nifty-generators jruby-openssl

That should be plenty to get you going on most any Rails/Ruby project.  Watch the output as the gems are installed, rarely should you run into trouble.  If a gem is complaining about installing you can add the –platform=java flag to the end to try and force it to install under the Java platform (for example):

>> jruby -S gem install grumpy_gem --platform=java

Connecting to Postgres

You probably can use any Rails supported database, but I prefer Postgres.  It’s a free database that is available on a variety of platforms (much like MySQL) but a little more powerful.  I also find it easier to work with on JRuby so it’s the one I’ll use here.

Run the PostgreSQL installer.  Follow the on-screen directions.  During the installation, however it is important to make sure you install the optional JDBC drivers, don’t forget this step (if you do you will have to run the Postgres Stackbuilder application later and install them).

The installer is going to add a ‘postgres’ user onto your system and require you to restart at some point.  Once the installation is completely finished you can open your command prompt and install the Rubygem for connecting to Postgres:

>> jruby -S gem install activerecord-jdbcpostgresql-adapter

I am not going to go into details about how to set-up users, etc. in Postgres.  You hopefully are familiar enough with database software to know how to do there is plenty of information on the Web to figure this out, just Google it.  Postgres comes with some nice GUI tools that make it easy to do and it is pretty straight forward.

Now there is one caveat with the Postgres ActiveRecord adapters and JRuby.  There’s a known issue with creating and dropping databases in Rails.  So in order to get things like rake db:create:all to work you need to type inside your Rails application root:

>> jruby -S script/generate jdbc

That is pretty much all there is to getting a full stack up and running on Windows.  I have started developing in the past week using JRuby on Windows along with Rubymine as my editor and GIT as my source control.  I’ve been pretty happy with it thus far.  I still enjoy developing most on my MacBook Pro – but it was a fun experience exploring JRuby (I’ve been playing with JRuby and Ant working together).

4-Bit Server VM 1.6.0_17) [amd64-java]C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\Wind
owsPowerShell\v1.0\;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files
(x86)\Git\cmd;C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\C:\Users\Nicholas>echo %PATH%
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\Wind
owsPowerShell\v1.0\;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files
(x86)\Git\cmd;C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\bin

Nokogiri – Cut with Precision

November 15th, 2009

Many times we as developers have to deal with complex data, be it an ActiveResource result set or a HTML/XML document.  Trying to parse data out of these using for each and nesting loops within loops can be cumbersome.  A more elegant solution is to use nokogiri and xpath.

Nokogiri is a type of Japanese saw, it also is a gem in Ruby that you can use to easily deal with XML or HTML documents.  (hint, ActiveRecord and ActiveResource objects both have to_xml methods).  You can easily install nokogiri (make sure you have libxml2 development packages installed, as the gem requires these to be properly built).

$ sudo gem install nokogiri

Now consider the following XML document: foods.xml

Before we can work with our data we need to read XML into Nokogiri. This is easy to accomplish:

> require 'rubygems'
> require 'nokogiri'
> doc = Nokogiri::XML.parse(File.read('foods.xml'))
=> #<Nokogiri::XML::Document:0x3f930c9db884 ...

What we are returned is a Nokogiri document which is a collection of Nokogiri elements and text objects. The document supports seaching (selecting a subset of nodes, or nokogiri nodeset) by both CSS selectors or XPath notation. These are returned as an array of elements and text objects.

So for example if we wanted to know all the names of the food items in our document we simply say:

> doc.xpath("//name").collect(&:text)
=> ["carrot", "tomato", "corn", "grapes", "orange", "pear", "apple"]

If we were interested in the entire node we could leave off the .collect(&:text). What if we wanted to select all the names of food items that were best baked?  This requires us to use what’s called an axis – we will first need to find the element “baked” but then go back up our XML elements to find which food the item is inside.

> doc.xpath("//tag[text()='baked']/ancestor::node()/name").collect(&:text)
=> ["pear", "apple"]

What if we were only interested in vegetables that were good for roasting?  Just add //veggies:

> doc.xpath("//veggies//tag[text()='roasted']/ancestor::node()/name").collect(&:text)
=> ["carrot", "tomato"]

What about if we wanted to know all the tags ‘corn’ had?  Again this is very easy:

> doc.xpath("//name[text()='corn']/../tags/tag").collect(&:text)
=> ["raw", "boiled", "grilled"]

We can even do searches matching the first character.  Let’s say we wanted to know all the food items that started with the letter ‘c’:

> doc.xpath("//name[starts-with(text(),'c')]").collect(&:text)
=> ["carrot", "corn"]

You have to admit this is pretty cool stuff.  You could also use [contains(text(),'rot'] and get back just carrot, useful when you want to do a partial match.  Axis combined with selectors give you a wide variety of options for parsing your dataset.  You can also match using operations.  See the links below for resources on the variety of options available.

Xpath is Powerful

Xpath lets us select XML elements, attributes and text without having to write cumbersome recursive, nested loops. Below are  links to online resources and tutorials.  The next time you have to dig through an XML document or ActiveResource result, don’t use recursive, nested loops; instead, consider a Japanese saw – nokogiri.

Learn More

Enjoy Working with Models using Hirb

November 1st, 2009

Hirb is a simple gem that lets you view objects as table (with their attributes as columns and their data as rows).  Collections are shown as a table.  Installing the gem is easy sudo gem install hirb.  The easiest way to handle including it into IRB is to add it to your .irbrc file in your home directory:

require 'rubygems'
require 'hirb'

Hirb::View.enable

Or you can leave off the last line, which will require you to type that into your console whenever you want it to turn on.  (This is the setting I use, because I am not always working with large data sets, but when I do I turn it on).

Another neat trick is to output your ActiveRecord SQL right into your console.  This is a nice if you’re wanting to optimize queries or you just are curious as to what ActiveRecord is really doing.  You could view the logs, but this puts everything into context.  Also if you don’t see a query happening you know that you’re still working with an in-memory object.  Type the below into your Rails console whenever you want to see SQL queries in-line with your ActiveRecord calls:

>> ActiveRecord::Base.logger = Logger.new(STDOUT)

You’ll end up with a console that looks like the image below.  Pretty nice if you have a lot of model work or just want to better visualize ActiveRecord calls and objects.

Rails console using Hirb and inline logger

Rails console using Hirb and inline logger

Learn More

http://tagaholic.me/2009/03/13/hirb-irb-on-the-good-stuff.html

Creating Your Own Gems

October 26th, 2009

Plugins have fallen out of style in the Rails world.  Gems are a better choice because they can be easily versioned and shared across projects.  Using Echoe you can easily package, install and deploy gems.  You can also checkout www.gemcutter.org, a next generation gem repository to host your gems.

Installing Echoe is easy, type ‘sudo gem install echoe‘ at your command prompt.  Echoe provides rake tasks and simplifies the creation of a gem.

You can create a project with the standard directory layout:

/mygem
../lib
../test
..Rakefile
..README.rdoc
..LICENSE
..CHANGELOG

You can optionally include a /bin or /ext if you have executables or extensions.  Just make sure at a minimum you have a Rakefile and lib folder.  You will also want to probably include a readme, license and changelog — it’s just the good citizen thing to do.

Next open your Rakefile and add the following template.  If you want to understand these options or add additional configurations you can reference Echoe’s documentation.

require ‘rubygems’
require ‘rake’
require ‘echoe’
Echoe.new(‘vowels’, ’0.0.1′) do |p|
p.description = ‘Tell whether or not a string starts with a vowel’
p.url = ‘http://www.ozmox.com/’
p.author = ‘Nicholas Cancelliere’
p.email = ‘ncancelliere@gmail.com’
p.ignore_pattern = ['tmp/*', 'script/*']
p.development_dependencies = []  # by default echoe adds itself as a dependency, we override this
end
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext
require 'rubygems'
require 'rake'
require 'echoe'

Echoe.new('vowels', '0.0.1') do |p|
  p.description = 'Tell whether or not a string starts with a vowel'
  p.url = 'http://www.ozmox.com/'
  p.author = 'John Doe'
  p.email = 'jdoe@gmail.com'
  p.ignore_pattern = ['tmp/*', 'script/*']
  p.development_dependencies = []
end

Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }

The configuration is pretty self-explanitory.  Once you have added this template to your Rakefile you can run ‘rake manifest‘ to generate a file manifest automatically.  The manifest is a list of all the files in your gem.

If you plan for people to use your gem you’ll want to write your own tests.  Many folks shy away from poorly tested gems.  Below is a simple module that adds both a class method and instance method.  It isn’t necessarily useful for a real-life situation, but I wanted to demonstrate especially how you create class-methods (since it isn’t as simple as adding self.method).

# lib/vowels.rb
module Vowels
  def self.included(base)
    base.extend ClassMethods
  end
  module ClassMethods
    def is_vowel?(str)
      ['a','e','i','o','u'].include?(str.slice(0,1).downcase)
    end
  end
  def is_vowel?
    "Sorry Vanna White doesn't live here."
  end
end

When this gem is installed, any class that includes Vowels will gain two methods: a class method is_vowel? and an instance method is_vowel?.  We create the class method by adding a module ClassMethods and using a little know-how of the Ruby object lifecycle.

Now that we have our gem written we need to update our manifest, generate a package and install it (build will both update your manifest and package in one action):

rake build
rake install

Now we should be able to utilize our new gem!

# config/environment.rb
config.gem "vowels", :lib => "vowels"

# app/models/contact
class Contact < ActiveRecord::Base
  include Vowels
end

$ contact = Contact.first
=> #<Contact id: 1, name: "Abby Smith">
$ contact.is_vowel?
=> "Sorry Vanna White doesn't live here!"
$ Contact.is_vowel?(contact.name)
=> true

As you can see from the above demonstration, we have successfully created both a class and instance method.  Because our Vowels module is provided by a gem we can use it anywhere as long as we require rubygems and vowels.

That is all there is to creating gems!  Now go create something awesome…

Safely Handling Data in Ruby

October 17th, 2009

Are you Tainted?

Ruby has a special method that you can call on an object called tainted?, and it will return a boolean based on whether the data comes from an external source or not.  Values that come from the network, a file on disk, command line, etc. will be considered tainted.  A value that is derived from inside your Ruby code will not.

irb> z = 20 + 50
=> 70
irb> z.tainted?
=> false
irb> b = File.open("sample.txt").readlines.first
=> "This is an example text file.\n"
irb> b.tainted?
=> true

Even when you combine data from inside Ruby with tainted data you’ll still get something that’s tainted…

irb> n = b + " More text."
=> "This is an example text file.\n More text."
irb> n.tainted?
=> true

One thing to note, and this is important, is that datastructures themselves may not be tainted, but the data they contain are.  You cannot know this without inspecting each individual piece of data (the array below has two pieces of tainted data):

irb> y = [n, z, b]
=> ["More text. This is an example text file.\n", 40, "This is an example text file.\n"]
irb> y.tainted?
=> false

You can also force data to be considered untainted by using the untainted method.  And likewise you can force data to be tainted by using the tainted method.

irb> safe = n.untaint
=> "This is an example text file.\n More text."
irb> safe.tainted?
=> false

$SAFE (Safe Levels)

Another feature in Ruby are Safe Levels.  This is represented by a special variable $SAFE.  The default in Ruby is 0, which really provides no safety.  You can increase the $SAFE level in your program by setting the value of $SAFE to be higher, but once you do so you cannot lower it (this is for security purposes).  Below is a brief description of the different safety levels.

  • 0 – No restrictions. (This is the default value.)
  • 1 – Potentially unsafe methods can not use tainted data.  Current directory is not added to Ruby’s search path for loading libs.
  • 2 – Ruby will not load any external files from globally writeable locations on the filesystem.  File#chmod, Kernel#fork, and Process::setpriority methods are also disabled.  All restrictions from 1 included.
  • 3 – Any newly created objects, even those from within the program, are considered tainted automatically.  You also cannot untaint objects.  All restrictions for 1 and 2 included.
  • 4 – Non-tainted objects created prior to the safe level being set cannot be modified.  All restrictions from 1, 2 and 3 included.