It is time to stop using acts_as_taggable

Have you ever added a tagging functionality to your Rails application? Then you have probably used either the acts_as_taggable gem, or its younger brother the acts_as_taggable_on gem.

These two gems are great, but they have some drawbacks that were unavoidable during the time of creation. Both of these gems rely on an RDBMS database that generally looks like this:

Tagging, RDBMS style

ActAsTaggable in all of its generations – was based on this model schema:

  • a Tags table that held the information about a specific tag (Basically, only the tag name)
  • a Taggings table, that held polymorphic associations references to the tagged instance (taggable) and the tagger instace (tagger).

So basically, when you wanted to get a tag list for some kind of a taggable instance or to see all the tags a tagger had made, you’d have to JOIN those 2 tables together. always.

Now, Continue reading “It is time to stop using acts_as_taggable”

It is time to stop using acts_as_taggable

Have you ever added a tagging functionality to your Rails application? Then you have probably used either the acts_as_taggable gem, or its younger brother the acts_as_taggable_on gem.

These two gems are great, but they have some drawbacks that were unavoidable during the time of creation. Both of these gems rely on an RDBMS database that generally looks like this:

Tagging, RDBMS style

ActAsTaggable in all of its generations – was based on this model schema:

  • a Tags table that held the information about a specific tag (Basically, only the tag name)
  • a Taggings table, that held polymorphic associations references to the tagged instance (taggable) and the tagger instace (tagger).

So basically, when you wanted to get a tag list for some kind of a taggable instance or to see all the tags a tagger had made, you’d have to JOIN those 2 tables together. always.

Now, joining isn’t really bad – it is there for a reason – but it could be one of some serious issues arising from this schema in certain circumstances.

1. JOINing tables from different servers

What happens when you have 10M tags and 40M taggings? your MySQL / Postgres / You-name-it-db needs some kind of an extended server setup that includes more than one instance of your db server, and if you are splitting the data – you might want to split your data and JOIN between 2 database servers.

Yes, it is possible, MySQL supports the Federated Storage Engine that allows you to join and share query information between 2 or more servers, MSSQL has the linked-servers feature that is very similar to that and some of the other databases have it.
The problem with this feature is that is far from being easy and simple to setup or maintain so by default if you are have a lot of tags or tagging and you want to add some sharding to the party, you are in a jiffy.

2. Indexing polymorphic association columns

Although these gems provide the necessery indices as part of the migration generator template, the fact that polymorphic association in Rails is composed out of a string (taggable_type) and an integer (taggable_id) is making the index’s diversity ratio rather low – meaning there are too many similar grouped entries in the index.

3. Autocomplete

Back to the 10M tags in the table example. Providing an autocomplete engine for this size of a table is horrific. You’ll have to use some kind of a full text engine like Solr or ElasticSearch to provide matching tags in real time.

4. Uniquness of tag name

How do you know if you create a newly provided tag or if you need to add tagging to an existing one? you first have to find if the tag exists already. 10M tags? good luck. Again, a full text search engine will provide a decent solution to this problem.

The solution: Redis

I love Redis. When it fits, it sits. If you are using Redis superpowers when you need to use them – it is an awesome tool. Redis provides several value types, each of them has it’s own superpower aimed for a specific problem – SET being the one we chose.

Storing tags in Redis for easy access

Redis Sets are basically arrays with unique members, for the following example we will use User as the tagger class, and Photo as the tagged class. Noticed there aren’t Tag or Tagging classes? We don’t need them anymore.

When User with ID 10 is tagging the Photo with ID 9 with the tag “Dog” we simply create a bunch of Redis sets that will allow easy access to any slice of data we might need:

Storing tags in redis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Add specific tagging of photo by a user
$redis.sadd "user:10:photo:9:tags", "dog"

# Add to photo specific tag set
$redis.sadd "photo:9:tags", "dog"

# Add a list of tagged photos to a tag set
$redis.sadd "tag:dog:photos", 9

# a list of photos tagged by a specific user
$redis.sadd "user:10:tagged_photos", 9

# Increase the usage counter for the "dog" tag

$redis.inc "tagged_by:dog"

Now we can have simple accessors to this information, for example:

photo.rb
1
2
3
4
5
6
7
class Photo

  # Get tags
  def tags
    $redis.smembers "photos:#{self.id}:tags"
  end
end

or for the Tag class:

tag.rb
1
2
3
4
5
class Tag
  def tagged_photo_ids
    $redis.smembers "tag:#{self.name}:photos"
  end
end

Generally, this is just an outline with a single rule – Normalize your data – instead of doing complicated join queries use a simple namespaced key value access to your data.

Ok, no joins. what about autocomplete?

Autocomplete is a PITA, but by using redis – We can maintain a list of your tag prefixes as keys to tags lists, for example the tag “liverpool” will be broken in to smaller pieces:

autocomplete.rb
1
2
3
4
5
6
7
$redis.add "tags:start_with:liv", "liverpool"
$redis.add "tags:start_with:live", "liverpool"
$redis.add "tags:start_with:liver", "liverpool"
$redis.add "tags:start_with:liverp", "liverpool"
$redis.add "tags:start_with:liverpo", "liverpool"
$redis.add "tags:start_with:liverpoo", "liverpool"
$redis.add "tags:start_with:liverpool", "liverpool"

This breakdown will allow us to easily access the list of tags (3 letters and up):

tag.rb
1
2
3
4
5
6
7
8
class Tag
  ...
  def Tag.tags_starting_with(tag_starts_with = "")
    $redis.smembers "tags:start_with:#{tag_starts_with}"
  end
end

Tag.tags_starting_with("liver") # => ["liver", "liverpool", "liverani",...]

Intersections!

Redis can provide an intersection between 2 sets, meaning you can “merge” between 2 sets and find either the indentical or different elements in both sets.

For example – if we would like to know which photos are tagged by both “dog” and “cat” will intersect those 2 sets.

intersection
1
$redis.sinter "tag:cat:photos", "tag:dog:photos" # => [12,93,94, ...]

Conslusion

Again, this is just an outline. There are many improvements to be added but we at ShinobiDevs are working on releasing a gem that could do just that – ideas are welcome.
Redis is a powerfull tool, there is probably no need to store the tagged data in an RDBMS structure but to find a better one maybe just like the one suggested above.

It is time to stop using acts_as_taggable

Have you ever added a tagging functionality to your Rails application? Then you have probably used either the acts_as_taggable gem, or its younger brother the acts_as_taggable_on gem.

These two gems are great, but they have some drawbacks that were unavoidable during the time of creation. Both of these gems rely on an RDBMS database that generally looks like this:

Tagging, RDBMS style

ActAsTaggable in all of its generations – was based on this model schema:

  • a Tags table that held the information about a specific tag (Basically, only the tag name)
  • a Taggings table, that held polymorphic associations references to the tagged instance (taggable) and the tagger instace (tagger).

So basically, when you wanted to get a tag list for some kind of a taggable instance or to see all the tags a tagger had made, you’d have to JOIN those 2 tables together. always.

Now, joining isn’t really bad – it is there for a reason – but it could be one of some serious issues arising from this schema in certain circumstances.

1. JOINing tables from different servers

What happens when you have 10M tags and 40M taggings? your MySQL / Postgres / You-name-it-db needs some kind of an extended server setup that includes more than one instance of your db server, and if you are splitting the data – you might want to split your data and JOIN between 2 database servers.

Yes, it is possible, MySQL supports the Federated Storage Engine that allows you to join and share query information between 2 or more servers, MSSQL has the linked-servers feature that is very similar to that and some of the other databases have it.
The problem with this feature is that is far from being easy and simple to setup or maintain so by default if you are have a lot of tags or tagging and you want to add some sharding to the party, you are in a jiffy.

2. Indexing polymorphic association columns

Although these gems provide the necessery indices as part of the migration generator template, the fact that polymorphic association in Rails is composed out of a string (taggable_type) and an integer (taggable_id) is making the index’s diversity ratio rather low – meaning there are too many similar grouped entries in the index.

3. Autocomplete

Back to the 10M tags in the table example. Providing an autocomplete engine for this size of a table is horrific. You’ll have to use some kind of a full text engine like Solr or ElasticSearch to provide matching tags in real time.

4. Uniquness of tag name

How do you know if you create a newly provided tag or if you need to add tagging to an existing one? you first have to find if the tag exists already. 10M tags? good luck. Again, a full text search engine will provide a decent solution to this problem.

The solution: Redis

I love Redis. When it fits, it sits. If you are using Redis superpowers when you need to use them – it is an awesome tool. Redis provides several value types, each of them has it’s own superpower aimed for a specific problem – SET being the one we chose.

Storing tags in Redis for easy access

Redis Sets are basically arrays with unique members, for the following example we will use User as the tagger class, and Photo as the tagged class. Noticed there aren’t Tag or Tagging classes? We don’t need them anymore.

When User with ID 10 is tagging the Photo with ID 9 with the tag “Dog” we simply create a bunch of Redis sets that will allow easy access to any slice of data we might need:

Storing tags in redis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Add specific tagging of photo by a user
$redis.sadd "user:10:photo:9:tags", "dog"

# Add to photo specific tag set
$redis.sadd "photo:9:tags", "dog"

# Add a list of tagged photos to a tag set
$redis.sadd "tag:dog:photos", 9

# a list of photos tagged by a specific user
$redis.sadd "user:10:tagged_photos", 9

# Increase the usage counter for the "dog" tag

$redis.inc "tagged_by:dog"

Now we can have simple accessors to this information, for example:

photo.rb
1
2
3
4
5
6
7
class Photo

  # Get tags
  def tags
    $redis.smembers "photos:#{self.id}:tags"
  end
end

or for the Tag class:

tag.rb
1
2
3
4
5
class Tag
  def tagged_photo_ids
    $redis.smembers "tag:#{self.name}:photos"
  end
end

Generally, this is just an outline with a single rule – Normalize your data – instead of doing complicated join queries use a simple namespaced key value access to your data.

Ok, no joins. what about autocomplete?

Autocomplete is a PITA, but by using redis – We can maintain a list of your tag prefixes as keys to tags lists, for example the tag “liverpool” will be broken in to smaller pieces:

autocomplete.rb
1
2
3
4
5
6
7
$redis.add "tags:start_with:liv", "liverpool"
$redis.add "tags:start_with:live", "liverpool"
$redis.add "tags:start_with:liver", "liverpool"
$redis.add "tags:start_with:liverp", "liverpool"
$redis.add "tags:start_with:liverpo", "liverpool"
$redis.add "tags:start_with:liverpoo", "liverpool"
$redis.add "tags:start_with:liverpool", "liverpool"

This breakdown will allow us to easily access the list of tags (3 letters and up):

tag.rb
1
2
3
4
5
6
7
8
class Tag
  ...
  def Tag.tags_starting_with(tag_starts_with = "")
    $redis.smembers "tags:start_with:#{tag_starts_with}"
  end
end

Tag.tags_starting_with("liver") # => ["liver", "liverpool", "liverani",...]

Intersections!

Redis can provide an intersection between 2 sets, meaning you can “merge” between 2 sets and find either the indentical or different elements in both sets.

For example – if we would like to know which photos are tagged by both “dog” and “cat” will intersect those 2 sets.

intersection
1
$redis.sinter "tag:cat:photos", "tag:dog:photos" # => [12,93,94, ...]

Conslusion

Again, this is just an outline. There are many improvements to be added but we at ShinobiDevs are working on releasing a gem that could do just that – ideas are welcome.
Redis is a powerfull tool, there is probably no need to store the tagged data in an RDBMS structure but to find a better one maybe just like the one suggested above.

WAL-E and Continuous Protection with Heroku Postgres

Heroku Postgres is Heroku’s database-as-a-service product. With Heroku Postgres, you can easily provision and scale a Postgres database for your Heroku application, or as a stand-alone service. Recently, we’ve blogged about PostgreSQL 9.3 on Heroku and how you can use Heroku Postgres dataclips to build awesome business dashboards with your data. In this post, we talk about how Heroku Postgres delivers continuous protection for your business data using WAL-E, an open source application for archiving PostgreSQL WAL (Write Ahead Log) files quickly, continuously and with a low operational burden.

About Continuous Protection

In order to protect customer data and be resilient to failure modes, we provide all Heroku Postgres databases with continuous protection by replicating data to external storage. We store a base backup of your database as well as a log of all committed transactions in the form of WAL files – the standard method for ensuring data durability in Postgres. This means we can restore your database by fetching the base backup and replaying all of the WAL files on a fresh install in the event of hardware failure, data corruption or other failure modes.

In addition to providing recovery functionality, this methodology lets us expose Heroku Postgres features that make working with your database faster and more flexible. The same mechanism underlies our fork feature, which lets you immediately create a perfect, byte-for-byte clone of your database for easier database migrations, load testing, and creation of development databases. It’s also how we let you spin up followers – read-only replicas of your database you can use for scaling reads, manual failovers and business reporting with no impact to production. These features are used far more often than restoring for disaster recovery, with the added benefit that they’ve let us battle-test the shared underlying mechanism so we can ensure success when disaster does hit. For more information on continuous protection on Heroku, check out the Dev Center article.

Introducing WAL-E

In addition to using tons of open source software here at Heroku, we are also serious and passionate about contributing code back to the community. WAL-E is one such open-source project that performs continuous, automatic archiving of WAL files to S3 across our entire fleet of databases. Initially developed by our resident tuple groomer Daniel Farina, you can now find it on GitHub. WAL-E is quickly becoming the default option for those running PostgreSQL on top of Amazon Web Services, with companies like Instagram using it to perform point-in-time restorations and quickly bootstrap a new read-replica or failover slave and Amazon themselves recommending it.

We wrote the initial version of WAL-E because we needed a programmatic, efficient way to ensure the safety of databases at scale across our entire user base. One of the major requirements was that the software would be easy to set up and administrate – as a result, WAL-E users can generally get up and running in about 15 minutes. In fact, WAL-E only has four operators: backup-fetch and backup-push, which retrieve and send, respectively, base backups; and wal-fetch and wal-push, which send and retrieve the WAL files themselves.

If you use Heroku Postgres, WAL-E is already hard at work for you as the underlying mechanisms for fast and easy disaster recovery, forks, and followers. Those using WAL-E for a private Postgres installation can set it up to archive logs from every few minutes to once a day, depending on their business needs.

Future of WAL-E

WAL-E is at the heart of our continuous protection we provide for our customers, and will be around for some time to come.

We actively maintain WAL-E and accept pull requests, issues and feedback on GitHub and on the mailing list. If you’re interested in contributing we’d love to hear from you.

For more on Heroku Postgres and continuous protection, check out the Dev Center article, which also talks about logical backup options with Heroku Postgres.

Special Thanks

Finally we want to offer a special thanks to contributors of WAL-E to date:

Seven Concurrency Models in Seven Weeks; Working with Ruby Threads

Seven Concurrency Models in Seven Weeks now in beta; Working with Ruby Threads now available

An Introduction to Go programming – A Free Online Course

An Introduction to Go programming – A Free Online Course

You should learn a programming language every year, as recommended by The Pragmatic Programmer. By learning a new language, you’ll broaden your perspective of programming. In 2010 we brought to you Clojure and now we offer you a free introductory course on Go programming.

You will learn Go programming along with RubyLearning’s mentors Brad Coish, George Thompson, Michael Kohl, Satish Talim, Tarun Jangra, Victor Goff and Willian Molinari. In the last few days, we already have over 100 participants registered for this course and more are joining. Have you?

What’s Go?

Go Go is an open source programming environment that makes it easy to build simple, reliable, and efficient software. Go has the feel of a dynamic language like Ruby or Python, but has the performance and safety of languages like C or Java. It is a completely open-source language, distributed with a BSD license, so it can be used by everybody even for commercial purposes without a fee, and it can even be changed by others.

What Will I Learn?

In this introductory course, you will learn the essential features of Go programming that you will end up using every day.

Who’s It For?

Anyone with some programming experience in any programming language.

Dates

The course is ongoing and you can join anytime you want. Also, you can log into the course area at a time convenient to you.

How do I register?

  • You first create an account on rubylearning.org
  • To enroll into the course, use the enrollment key GoP101 We are over 215 participants and have now closed the registration process. If however you still want to join, then email me at satish [at] rubylearning.org

Course Fees

The course is completely free of cost.

Hurry, registrations have started. Remember, the idea is to have fun learning Go.

At the end of this course you should have all the knowledge to explore the wonderful world of Go on your own.

Technorati Tags: , ,


(Powered by LaunchBit)

How SpaceGlasses Builds the Future with Heroku

Editor's note: This is a guest post from Michael Buckbee of Meta/SpaceGlasses.

SpaceGlasses are augmented reality glasses that actually work. They let people control systems with a gesture, see virtual objects on top of the real world and create technology that would make Tony Stark proud.

Prior to joining Meta, I had developed and managed a number of high traffic Rails sites. I was brought on to help move the company’s website from a single static launch page to being an e-commerce platform and to help lay the groundwork for the company’s app store.

We chose to build on top of Heroku as we are moving very quickly, need to have a lots of flexibility and don’t have the time or budget to provision our own infrastructure from scratch.

As the site’s traffic has rapidly grown from tens of thousands to hundreds of thousands of visitors a day being on Heroku has only made more sense.

Meta

Beyond just manually tweaking the number of web dynos that were running we were able to take advantage of a number of other Heroku add-ons and services to great effect:

1. Memcache as a Service

One of the first steps we took was to start caching everything possible in Memcached, facilitated by the Memcachier Add-on. We started by using Rails built in Action Caching to reduce the load that underlying Unicorn servers running inside the dynos needed to function. On several pages with heavy database interaction results were cached separately. However, one of the somewhat biggest wins for us was using Memcached as a buffer to keep near real time order information from our e-commerce provider readily available.

2. Sysadmin in a Box

Being the sole full time web-developer on the project (all the other software and electrical engineers are working feverishly making the actual glasses), it was tremendously valuable for us to be able to scale at whim. While I certainly could have taken the time to setup HAProxy and SSL, and a series of AppServers and Postgres, having Heroku available was like having an on-demand sysadmin available 24 hours a day.

3. Scaling as a Service

Early on, we had added the AdeptScale service to our production Heroku stack. AdeptScale dynamically increases the number of running web dynos in relation to response time metrics.

If response times increase, more and more dynos are added. We continuously monitor the site and were surprised one day to find that 30 dynos had been spun up.

Turning to Google Analytics we were a little flabbergasted to realize that we had climbed up to comfortably handling 3000 active visitors to the site without even realizing it.

Some investigation turned up that we had become a major topic of conversation on 4Chan that day. The 120,000 visitors that came to the site over the next 24 hours from 4Chan were a mix of the genuinely curious, aggressively hostile and quite a few with a real vision of the future. All of which we sailed through because of AdeptScale and Heroku.

Building The Future

Heroku has been a lifesaver in allowing us to book hundreds of thousands of dollars of sales we would have otherwise lost out on due to downtime. As we start launching new services and products it’s a huge comfort to know we just don’t have to worry about our hosting or our ability to handle the future.

Practical Programming; The Retrospective Handbook

Practical Programming now in print and shipping; The Retrospective Handbook now available

Monday Morning Me: Sep 16, 2003

Here’s what’s going on.

WindyCityRails

Had another great time at WindyCityRails. I enjoyed the talks, and it was great to meet new people/see people I don’t see enough in person. As usual, Ray and the WCR team had the logistics of the event down (even the WiFi was pretty fast for a conference.) The venue is really nice, if maybe not perfectly arranged for the kinds of talks being given.

I very much loved Aaron Kalin waling around in his stormtrooper suit on day two, in response to a bet from Table XI to donate $50 to Facing Disability for each hour that Aaron wore it. You can see some of the results: here, here, here, here, and here.

As for my own talk (slides, video coming eventually, I assume), I was happy with it, but hope I get to give it again,

Continue reading “Monday Morning Me: Sep 16, 2003”

Monday Morning Me: Sep 16, 2003

Here’s what’s going on.

WindyCityRails

Had another great time at WindyCityRails. I enjoyed the talks, and it was great to meet new people/see people I don’t see enough in person. As usual, Ray and the WCR team had the logistics of the event down (even the WiFi was pretty fast for a conference.) The venue is really nice, if maybe not perfectly arranged for the kinds of talks being given.

I very much loved Aaron Kalin waling around in his stormtrooper suit on day two, in response to a bet from Table XI to donate $50 to Facing Disability for each hour that Aaron wore it. You can see some of the results: here, here, here, here, and here.

As for my own talk (slides, video coming eventually, I assume), I was happy with it, but hope I get to give it again, because I feel like I was still discovering some ideas about how I was approaching the topic. For example, the framing idea of how practices get to become your new normal practice really only fully formed the night before, but I’m really interested in exploring it.

Also, as much as I really wanted to tell the “little web engine” story, I don’t think it’s as funny to anybody else as it is to me. (Right now, I have a much better time getting jokes to land in workshops then in formal talks, which is frustrating.)

Master Space and Time

The update is that today is the last day of the current pricing scheme. Tomorrow, the prices go up to $10 per book, or $25 for the bundle. So, if you want to get the cheaper price in under the wire, go to http://www.noelrappin.com/mstwjs.

Rails Test Prescriptions

I’m currently in the process of auditing the existing book to determine all the things I wrote that still make sense, and all the things I wrote that don’t.

I’d like the next version to hit the “good tests equal good design” button a lot harder, and I’m trying to see how to structure the book to get that to work.

I’m also trying to figure out the minitest/RSpec balance. I’ll describe the basics of both, but I think I need to pick one as the primary focus of the code samples.

In favor of minitest: it’s the Rails default, it’s arguably simpler (or at least, it’s simpler internally), and the existing RTP code examples are compatible.

In favor of RSpec: it probably still has a larger share of Rails developers, it’s more expressive, and it has a more interesting ecosystem.

Anybody with an argument for focusing on one or the other that goes beyond “I use that one” is welcome to chime in.

Trust-Driven Development

I’m Doing an edit/finish some half-finished sections. I’d like to get a 30-some-odd page initial release out sometime in the next 10 days. Again, if you want to get it before the price goes up, it’s available at http://www.noelrappin.com/trdd.

Sinatra and Google Currency API – Part 2

Sinatra and Google Currency API – Part 2

This guest post is by Girish Sonawane, a self-taught programmer. He came across Ruby in 2008 and has since been working full-time on Ruby. He worked as a Rails freelancer and later co-founded Cube Root, an exclusive Ruby on Rails software boutique catering to outsourced work. His interests are everything related to technology or science. You can reach him at girish@cuberoot.in or via twitter @girishso.

Girish Sonawane In this two-part series, I will show you how to use Google currency conversion API and use it in a small Sinatra app. In Part 1 we built a small function to access the Google API for currency conversion. This is Part 2, where we will build a small Sinatra app using the function we created in Part 1.

The source code for this series is available on Github, with commits for each step. This little library is available as a Ruby Gem at GoogCurrency.

Wireframes

We will have two screens, one with a form where a user can submit data and another showing the result.

Home page

Home

Results page
Results

Sinatra

Since this is a very basic app, we are going to use Sinatra in classic style. We need following files.

Gemfile for all the gem dependencies.

1 source 'https://rubygems.org'
2
3 gem "rest-client", "1.6.7"
4 gem "json", "1.8.0"
5 gem "rspec", "2.14.1"
6 gem "fakeweb", "1.3.0"
7 gem "sinatra", "1.4.3"
8 gem "capybara", "2.1.0"
9 gem "haml"

config.ru required to run the app on Rack servers like Passenger, Heroku etc.

1 require 'bundler'
2 Bundler.require
3 require './app'
4 run Sinatra::Application

Bundler.require will require all the gems listed in our Gemfile and made available to our Sinatra app.

Let’s create an empty app.rb, the main Sinatra app file with touch app.rb

Continuing with the same spirit as Part 1, we are going to test drive this app. We are going to use capybara for acceptance tests.

spec/acceptance_spec.rb

1  require 'bundler'
2  Bundler.require
3
4  disable :run
5  set :root, File.dirname(__FILE__) + "/.."
6  require Sinatra::Application.root + '/app'
7
8  Capybara.app = Sinatra::Application
9
10 RSpec.configure do |config|
11   config.include Capybara::DSL
12 end
13
14 describe 'currency converter' do
15   it "loads currency converter form"
16   it "converts currencies"
17   it "handles errors"
18 end

Line 4, disables the web server, we don’t need a webserver for specs. Line 8, tells capybara it’s a Sinatra app. Line 11, makes capybara DSL available to our specs.

Line 15, 16, 17, adds three pending specs.

If we execute our spec now with rspec spec/acceptance_spec.rb, it says:

1  ***
2
3  Pending:
4    currency converter loads currency converter form
5      # Not yet implemented
6      # ./spec/acceptance_spec.rb:15
7    currency converter converts currencies
8      # Not yet implemented
9      # ./spec/acceptance_spec.rb:16
10   currency converter handles errors
11     # Not yet implemented
12     # ./spec/acceptance_spec.rb:17

Let’s define the first spec and make it pass.

1 it "loads currency converter form" do
2     visit "/"
3     page.should have_content("Currency Converter")
4     find('form').should have_button('Convert')
5   end

capybara simulates user interactions with the website. visit "/" takes the user to the home page of the site as expected. Line 3, checks for existence of “Currency Converter” text on the page. Line 4, expects a form with the Convert button.

Typically, there is no need of such granular level testing, but this tells us if the test suite is working as expected.

Executing the spec fails with:

1 expected #has_content?("Currency Converter") to return true, got false

Now to make this spec pass, let’s modify app.rb:

1 get "/" do
2     haml :"index"
3   end

get "/" loads the home page. We are using haml view templates instead of erb. It expects views/index.haml in views folder. Let’s add it.

1 %h1 Currency Converter
2
3   %form(action = "/convert" method = "post")
4
5       %input#convert(type="submit" value="Convert")

Let’s also add a layout views/layout.haml:

1 !!!
2   %html
3     %head
4       %title Currency Conversion Tutorial
5     %body
6       = yield

Executing specs with rspec spec/acceptance_spec.rb passes the spec. We are good to go with the next pending spec.

1  valid_response =<<-VALID
2    {lhs: "1 U.S. dollar",rhs: "54.836587 Indian rupees",error: "",icc: true}
3    VALID
4    .
5    .
6    .
7    it "converts currencies" do
8      FakeWeb.register_uri(:get,
9                         "http://www.google.com/ig/calculator?hl=en&q=1USD=?INR",
10                        :status => "200",
11                        :body => valid_response)
12     visit '/'
13
14     fill_in "amount", :with => 1
15     select "USD", :from => "from"
16     select "INR", :from => "to"
17     click_button 'Convert'
18
19     find("#result").should have_content('54.836587')
20   end

We are again using fakeweb gem to simulate the Google API interaction, Line 8.

We are simulating user converting 1 USD to INR, using capyabara DSL to simulate the user interactions

Line 12, visit home page.

Line 14, Fill amount input field with 1.

Line 15, select USD from from currencies select box.

Line 16, select INR from to currencies select box.

Line 18, click button “Convert”

Line 20, we expect to have #result with the converted amount.

Executing spec fails with:

1 Unable to find field "amount"

Let’s go ahead and add the amount field and other fields as well in index.haml.

1  %h1 Currency Converter
2
3  %form(action = "/convert" method = "post")
4    %fieldset
5      %legend
6      From
7      %input#amount(name="amount")
8
9      %select#from(name="from")
10       %option(value="inr") INR
11       %option(value="usd") USD
12       %option(value="eur") EUR
13
14     To
15     %select#to(name="to")
16       %option(value="inr") INR
17       %option(value="usd") USD
18       %option(value="eur") EUR
19
20     %input#convert(type="submit" value="Convert")

Executing the spec now fails with:

1 Unable to find css "#result"

To fix this, we need to add post "/convert" handler in app.rb:

1 post "/convert" do
2   @result = GoogCurrency.send("#{params[:from]}_to_#{params[:to]}".to_sym, params[:amount])
3   haml :"convert"
4 end

Line 2, we are generating the GoogCurrency method to call dynamically. params[:form], params[:to] have the from and to currencies respectively. params[:amount] has the amount to convert. "#{params[:from]}_to_#{params[:to]}" gets converted to usd_to_inr in our case. But how do we invoke this method? In Ruby, we don’t invoke methods, we send a message to the object and the object responds to the message. To invoke this method we send message usd_to_inr to GoogCurrency, along with the method parameter (amount).

Then we render haml template convert.

convert.haml

1 %div
2   #{params[:amount]} #{params[:from]} =
3   %span#result= @result
4   #{params[:to]}
5
6 %a(href="/") Back

The spec now passes.

Now, we only have one more spec left i.e. “currency converter handles errors”. Let’s get at it.

1  it "handles errors" do
2      invalid_response =<<-INVALID
3      {lhs: "",rhs: "",error: "4",icc: false}
4      INVALID
5      FakeWeb.register_uri(:get,
6                           "http://www.google.com/ig/calculator?hl=en&q=xyzUSD=?INR",
7                           :status => "200",
8                           :body => invalid_response)
9
10     visit '/'
11
12     fill_in "amount", :with => "xyz"
13     select "USD", :from => "from"
14     select "INR", :from => "to"
15     click_button 'Convert'
16
17     find("#error").should have_content("An error occurred: 4")
18   end

It’s similar to the earlier spec, but Fakeweb now returns an error response, because the amount is invalid. Executing the spec now fails with:

1 Unable to find css "#error"

To make this spec pass, let’s handle the exception raised by GoogCurrency in app.rb:

1 post "/convert" do
2     begin
3       @result = GoogCurrency.send("#{params[:from]}_to_#{params[:to]}".to_sym, params[:amount])
4     rescue Exception => ex
5       @error = ex.message
6     end
7     haml :"convert"
8   end

Here, we’re rescuing the exception and setting @error instance variable.

Now in convert.haml, let’s display the error message.

1 -if @error
2   #error= @error
3 -else
4   %div
5     #{params[:amount]} #{params[:from]} =
6     %span#result= @result
7     #{params[:to]}
8
9 %a(href="/") Back

All the specs now pass. Note, we haven’t opened the browser manually even once! Let’s do it and hope everything is fine and dandy! Execute the command rackup -p 4567 and visit localhost:4567.

That’s it!

Feel free to ask questions and give feedback in the comments section of this post. Thanks!

Technorati Tags: , ,


(Powered by LaunchBit)

Ruby Programming 46th Batch: Registrations are now open

Registrations are now open for RubyLearning’s popular Ruby programming course. This is an intensive, online course for beginners that helps you get started with Ruby programming. The course starts on Saturday, 28th Sept. 2013 and runs for seven weeks

Course Fee

Please create a new account first and then pay US$ 44.95 (for the first 10 participants, after which the course fee will be US$ 69.95) by clicking on the PayPal button Paypal


Download ‘Advice for Ruby Beginners’ as a .zip file.

Here is what Sandra Randall (Butler), a participant who just graduated, has to say – “You kindly offered me the opportunity to join your Ruby course. I’m new to development and found the course, even though basic for programmers, a little tricky for me. I managed to complete all of the assessments and really learnt a lot. Thank you very much for the opportunity. It has really given me the push I needed to learn Ruby and I’m currently treading my way through both the pickaxe and Agile Development books and enjoying it. I’ve recently been offered a position as a Junior Systems Developer at a local Software house in South Africa – all thanks to the push you gave me which gave me the motivation and drive to get going.”

What’s Ruby?

Ruby

According to http://www.ruby-lang.org/en/ – “Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. Ruby’s elegant syntax is natural to read and easy to write.”

Yukihiro Matsumoto, the creator of Ruby, in an interview says –

I believe people want to express themselves when they program. They don’t want to fight with the language. Programming languages must feel natural to programmers. I tried to make people enjoy programming and concentrate on the fun and creative part of programming when they use Ruby.

What Will I Learn?

In the Ruby programming course, you will learn the essential features of Ruby that you will end up using every day. You will also be introduced to Git, GitHub, HTTP concepts, RubyGems, Rack and Heroku.

Some Highlights

RubyLearning’s IRC Channel

Some of the mentors and students hang out at RubyLearning’s IRC (irc.freenode.net) channel (#rubylearning.org) for both technical and non-technical discussions. Everyone benefits with the active discussions on Ruby with the mentors.

Google Hangouts

There is a Hangout Event that is open for students, for drop-in hangouts where students can pair program with mentors or with each other. This is often where you can get help with your system, editor, and general environment. Anything that can help you with your coding environment that you are having problems with are usually discussed interactively here.

Git Repositories

Shared (private) repositories available for those that want to learn git and the revision controlled programming workflow. This allows students that want to collaborate while learning. This is a great way to record your progress while learning Ruby.

eBook

The course is based on the The Ultimate Guide to Ruby Programming eBook. This book is priced at US$ 9.95. However, the Kindle edition of the eBook is available for US$ 6.

Challenges and Side Tracks

This is course material not found in the RubyLearning Study Notes nor in the E-Book! Depending on participation levels, we throw a Ruby coding challenge in the mix, right for the level we are at. We have been known to give out a prize or two for the ‘best’ solution.

Who’s It For?

A beginner with some knowledge of programming.

You can read what past participants / online magazines have to say about the course.

Mentors

Satish Talim, Michael Kohl, Satoshi Asakawa, Victor Goff III and others from the RubyLearning team.

Dates

The course starts on Saturday, 28th Sept. 2013 and runs for seven weeks.

How do I register and pay the course fees?

  • You can pay the course fees either by Paypal or send cash via Western Union Money Transfer or by bank transfer (if you are in India). The fees collected helps RubyLearning maintain the site, this Ruby course, the Ruby eBook, and provide quality content to you.

To pay the Course Fee:

Please create a new account first and then pay US$ 44.95 (for the first 10 participants, after which the course fee will be US$ 69.95) by clicking on the PayPal button Paypal

How does the course work?

For details on how the course works, refer here.

At the end of this course you should have all the knowledge to explore the wonderful world of Ruby on your own.

Remember, the idea is to have fun learning Ruby.

Technorati Tags: , , ,


(Powered by LaunchBit)

Programming Sound with Pure Data: Make Your Apps Come Alive with Dynamic Audio

Programming Sound with Pure Data: Make Your Apps Come Alive with Dynamic Audio now in Beta

Rails Test Prescriptions 2 Is Coming

I’m pleased and more than a little surprised to be announcing the existence of Rails Test Prescriptions 2, (which may not be the final title).

You have questions. Even if you don’t, I have answers.

Can you give the news in the style of a movie poster tag line?

Sure.

Coming in 2014. It’s time to get your prescription… refilled.

Rails Test Prescriptions 2: The re-prescriptioning.

When did this happen?

Really, just last week. I got an email asking if I was interested (Spoiler Alert: I was.), and we went very quickly through to contract.

What will it cover?

It’s a little up in the air still, but broadly, we’ve got this:

Rails Test Prescriptions 2 Is Coming

I’m pleased and more than a little surprised to be announcing the existence of Rails Test Prescriptions 2, (which may not be the final title).

You have questions. Even if you don’t, I have answers.

Can you give the news in the style of a movie poster tag line?

Sure.

Coming in 2014. It’s time to get your prescription… refilled.

Rails Test Prescriptions 2: The re-prescriptioning.

When did this happen?

Really, just last week. I got an email asking if I was interested (Spoiler Alert: I was.), and we went very quickly through to contract.

What will it cover?

It’s a little up in the air still, but broadly, we’ve got this:

  • Updates to the big tools. Rails 4, RSpec 3, minitest replacing TestUnit.
  • Changes to the ecosystem, so updates to a bunch of other tools, including FactoryGirl, Cucumber, Capybara
  • Some deprecations — there are some tools discussed that are basically not anymore.
  • New tools. I think the biggest class of tool not discussed in the original RTP is quick startup tools like Spork and Zeus. I’d like to cover continuous integration options, too, we’ll see if I can make that fit.

Over and above all of that, of course, is the fact that the discussion over good testing practice has changed. There’s a lot more talk about maintaining large test suites, writing faster tests, and using tests to drive better Rails application design. I’m planning to talk about that aspect of testing as well.

I am really open to feedback and ideas about what to include and what can be safely removed (any Rails performance testing fans in the audience?)

Timeframe?

Good question. I’d bet at least spring for the first beta. Ask me again in a few weeks.

Are you insane?

I assume you are referring to the fact that I have other writing projects in process.

Yeah, that. Well?

I’ve got it under control.

Yeah, I’m gonna need more than that, writer guy

Okay, let’s take them one at a time.

Okay. Master Space and Time with JavaScript: Ember

Well, the nice folk at Ember central were nice enough to release Ember 1.0, making my plan considerably more concrete than it might otherwise be.

I’m currently working on the draft-complete release of the Ember book, which I expect to release this week. Code up to date with Ember 1.0 and Ember Data 1.0 beta. Info on the Ember inspector, and more stuff on components and promises in Ember routing. Maybe another goodie or two if I have time. That should take me roughly to 115 – 120 pages, a complete draft, and it will trigger a price increase. If I release the book this week, the price increase will come about 9/16. You can still buy Master Space and Time With JavaScript with the handy-dandy purchase link.

The Ember team has announced plans to do new point releases every six weeks. My expectation is that through the end of 2013, I’ll fix errors, and come back toward the end of the year to see if there is anything that needs updating or adding. Probably the same through at least the beginning of 2014.

Okay. What about Trust-Driven Development?

That’s obviously a little more problematic. But I’m really happy with the book, it’s fun to write, and I’m going to keep up with it.

The initial release will still come in September, hoping for this week, but that one looks optimistic. Next week is a better bet. That release also triggers a price increase. And of course, you can still buy Trust-Driven Development at the fabulous purchase link.

After that, I’m not sure. One thing about this book, though, is that it is kind of a nice palette cleaner to break up writing just about code all the time, so I’m actually looking forward to having that as a project during breaks in the Rails Test Prescription 2 process, such as technical reviews and so on.

So maybe I am insane after all.

Anyway, I’m very much looking forward to revisiting Rails Test Prescriptions, fixing all the things I got wrong last time, and making all new mistakes. Hope you like it.

Rails Bugmash in Israel

Last week, in August 30 – We held a Rails hunt & destroy bugmash in Israel with the unbelieveable courtesy of Ebay Innovation Center in Israel.

This bugmash was born as a result of a discussion in the Israeli Rails group and lead to an amazing event,
About 40+ members of the Israeli Rails developers community, including awesome people from Google/Waze, Simplee, Fiverr, Scoreoid and a lot more freelancers and Rails lovers gathered in the Ebay innvation center in Israel at 9:00am (Yes, early as fuck) to hunt down some Rails bugs.

During this day, we used a special application we developed to allow people to choose and take ownership on Rails github issues – which resulted in 15 pull-requests and 1 merge to master so far.

It was an amazing event, that showed the power of the Israeli Rails community – and although we weren’t successful Continue reading “Rails Bugmash in Israel”

Rails Bugmash in Israel

Last week, in August 30 – We held a Rails hunt & destroy bugmash in Israel with the unbelieveable courtesy of Ebay Innovation Center in Israel.

This bugmash was born as a result of a discussion in the Israeli Rails group and lead to an amazing event,
About 40+ members of the Israeli Rails developers community, including awesome people from Google/Waze, Simplee, Fiverr, Scoreoid and a lot more freelancers and Rails lovers gathered in the Ebay innvation center in Israel at 9:00am (Yes, early as fuck) to hunt down some Rails bugs.

During this day, we used a special application we developed to allow people to choose and take ownership on Rails github issues – which resulted in 15 pull-requests and 1 merge to master so far.

It was an amazing event, that showed the power of the Israeli Rails community – and although we weren’t successful in finding a Rails core member willing to help us online – We managed to get something out of this day. A list of solved bugs and a wonderful day to Open Source.

I would like to thank the awesome guys (and girl) @ the Ebay innovation center, on crafting this event, getting us a south american mean buffet and providing us with an amazing office workspace that helped us achieve this wonderful result – I hope it wasn’t the last time that we are doing it.

You can read more on the Ebay innovation center blog post and see us in action in these pictures.

Hoping to see more of you there next time!

Rails Bugmash in Israel

Last week, in August 30 – We held a Rails hunt & destroy bugmash in Israel with the unbelieveable courtesy of Ebay Innovation Center in Israel.

This bugmash was born as a result of a discussion in the Israeli Rails group and lead to an amazing event,
About 40+ members of the Israeli Rails developers community, including awesome people from Google/Waze, Simplee, Fiverr, Scoreoid and a lot more freelancers and Rails lovers gathered in the Ebay innvation center in Israel at 9:00am (Yes, early as fuck) to hunt down some Rails bugs.

During this day, we used a special application we developed to allow people to choose and take ownership on Rails github issues – which resulted in 15 pull-requests and 1 merge to master so far.

It was an amazing event, that showed the power of the Israeli Rails community – and although we weren’t successful in finding a Rails core member willing to help us online – We managed to get something out of this day. A list of solved bugs and a wonderful day to Open Source.

I would like to thank the awesome guys (and girl) @ the Ebay innovation center, on crafting this event, getting us a south american mean buffet and providing us with an amazing office workspace that helped us achieve this wonderful result – I hope it wasn’t the last time that we are doing it.

You can read more on the Ebay innovation center blog post and see us in action in these pictures.

Hoping to see more of you there next time!

Joining AppsScrolls!

In the last couple of weeks I needed to boot up some new rails apps, all of them had the same skeleton structure including Devise, Bootstrap, RSpec and so on.

I remembered that there was an attempt to tackle this repetitive skeleton app generation process by Michael Bleigh’s RailsWizard so I pinged Intridea on Twitter and asked what was the status of it.

It seemed that RailsWizard isn’t maintained for a long time, but Dr Nic took over it a while ago and formed AppScrolls.

After a short conversation he happily added me to the contributors list so now I am a very proud co-maintainer of a very awesome gem.

Currently the plans are to add some missing scrolls, such as some mongo adapters and some testing frameworks. Hit me up if you are missing something – or even better, fork and pull-request.

Joining AppsScrolls!

In the last couple of weeks I needed to boot up some new rails apps, all of them had the same skeleton structure including Devise, Bootstrap, RSpec and so on.

I remembered that there was an attempt to tackle this repetitive skeleton app generation process by Michael Bleigh’s RailsWizard so I pinged Intridea on Twitter and asked what was the status of it.

It seemed that RailsWizard isn’t maintained for a long time, but Dr Nic took over it a while ago and formed AppScrolls.

After a short conversation he happily added me to the contributors list so now I am a very proud co-maintainer of a very awesome gem.

Currently the plans are to add some missing scrolls, such as some mongo adapters and some testing frameworks. Hit me up if you are missing something – or even better, fork and pull-request.