Sunday, May 25, 2014

How to: Override devise default routes for sign in and sign out

In one of my application, I need to override default routes provided by Devise for better SEO.

SEO expert asked me to change default url in such way,
"/users/sign_in" => "/sign-in"
"/users/sign_up" => "/sign-up"

And I know, it is possible in devise. Devise is awesome authentication gem which provides lots of customization as per our app needs. In fact, every gem publisher should study the devise code for better understanding on how to provide api's for customization.

So here is the full customization of default routes,

redirect section : 
        On line 5 and 6, I wrote a redirection rule for SEO purpose and for demonstration over here. But best place is to place redirection rules in your web server, that is in nginx or apache.
        This only applies to you, if your app is already live and search engine has already indexed your sign in and sign out routes.

skip section :
        On line 10, I have skipped generation of default routes for session(sign_in) and registration(sign_up).

devise_scope block :
        In devise_scope block, we need to provide our own urls for sign_in and sign_out. So we need to provide url format with controller and action.
        Also, for each urls, we need to provide path helper using ":as", so that we can still use old path name, without code change.

Tuesday, May 6, 2014

Upgrading from Rails 4.0 to rails 4.1 - My experience

TL;DR: I have recently upgraded one of my rails app from rails version 4.0 to 4.1 and collected steps over here.

Before starting upgrade I went through following link and it is definitely worth to read those links,
1. Rails 4.1 release notes
2. Rails 4.1 upgrade steps

These link almost covered every thing which we need to for rails 4.1 upgrade. Thanks to rails team.

While upgrading you have to note that, your app might be using some gems which is not yet rails-4.1 compatible or might be it's dependent gem.
In my case it was, "sidekiq-failures" which was not compatible with sidekiq-3.0.0 and sidekiq-3.0.0 was rails-4.1 compatible. So I have disabled "sidekiq-failures" for now. ( Author of gem is working on it and might be he has fixed it be the now ).

Here are my steps for rails upgrade.
1. bundle update :
        Bundle update is our routine process, but still I have started with it. So that my stack is ready with all latest gems. You can use "bundle outdated" to check which of your gems are outdated and how much. If major update is happening with any gem then you must have to look into its release notes.

2. Rails 4.1 update and bundle update again :
        Then I have updated rails version first and then bundle update again. Along with this update I have referred rails upgrade steps guide and followed steps which was applicable to my apps.

Out of that important once are,
1. Changes to config/secrets.yml
2. Changes to test helper
3. Addition of spring gem

These are important changes and you have to do it before proceeding.

3. Spec run and code fixes : 
        Running test cases over here is very much important to quickly identify issue with code.
Here are some of failing cases,
a. has_many with through relation :
one of my relation was failing with wrong query
relation was post has_many badges
has_many :badges, -> { order "badgings.id DESC" }, through: :badgings
and failing query is,
"SELECT "badges".* FROM "badges" ORDER BY badgings.id DESC"
So here I was ordering badges using through relation, which is indeed wrong, but I was surprised why it was working previously and not after upgrade. I tried to find cause but not able to find it.
Please comment here, if you know the cause.

For fixing I have changed above line to,
has_many :badges, -> { order "badges.id DESC" }, through: :badgings
b. Complex count quires :
The Major changes, I did with count queries in all pagination. I have changed
@tags.all.count
to
@tags.to_a.count
Please see https://github.com/rails/rails/pull/10710

4. Removed mail_view gem ( If you are using ): 
        Now mail_view gem is part of rails 4.1. As, author of mail_view gem has integrated his gem into rails so there is very less change need to do.
- remove mail_view routes mounting from routes 
- and search and replace MailView to ActionMailer::Preview

Apart from these steps, I have followed following steps which are only applicable if your are using sidekiq-3 or acts-as-taggable-on-3.1
1. For sidekiq :
If you are using Sidekiq for back-grounding and Capistrano for deployment, then your Capistrano script will fail if you have upgraded to Sidekiq-3. Since in Sidekiq-3, Capistrano integrated support has been removed.
To make Capistrano script to work, we need to use "capistrano-sidekiq" gem.
- Just add "capistrano-sidekiq" gem to Gemfile
- And replace "sidekiq/capistrano" to "capistrano/sidekiq" in Capfile.

2. For acts-as-taggable-on :
If you have upgraded acts-as-taggable-on-3.1.1 then you need to run the following generator,
rake acts_as_taggable_on_engine:install:migrations
Since acts-as-taggable-on has added some new columns in tagging system.

And done. Now my app is working on rails 4.1 very smoothly. 


Monday, October 14, 2013

Testing Responsive web design with Rspec

Recently I am working on an application where we have used zurb foundation for responsive web design. We have separated all devices screen broadly into three categories as follows,

1. small : Screen width upto 590.
2. medium : Screen width upto 1025.
3. large : Screen width uptp 1280.

Very soon we will add new category,
4. xlarge : above 1280+ ;)

Now all things are fine, but until you didn't write acceptance test, you can't make sure that your design is working fine for all screen.

Before taking responsive web design into consideration, we have already written feature specs using rspec + capybara (selenium).

So here we want handy configuration which will not affect existing feature spec and should treat existing specs meant to be written for large screen.

So here is, how I have added configuration for my rspec suite to target testing responsive design.


With above configuration, if you write any feature spec without :device_size then it will run that spec against 'large' screen. And if you want to write a spec for 'small' and 'medium' devices you can write by using metadata 'device_size => :small' or 'device_size => :medium'
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      # spec with default screen size i.e large
    end 

    scenario "abc", :js => true, :device_size => :small do
      # spec with small screen size
    end 
  end
With the help of 'config.include ScreenSize, :type => :feature' you can directly change screen size into example.
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      set_screen_size(:medium)
      # spec with default screen size i.e large
    end 
  end
If you want to some special configuration based on device size you can do that using,
  config.before(:each, :device_size => :small) do
    # special configuration .........
  end
TODO :
I want to split a test suite into feature/small/*.rb for small devices, feature/large/*.rb for large devices and so on.. and apply specific metadata configuration based on type of suite.

References :
http://www.blaulabs.de/2011/11/22/acceptance-testing-with-responsive-layouts

Sunday, September 22, 2013

Capybara wait for ajax call to finish

TL;DR: Before capybara 2.0.0, wait_until method was available which can be used for wait for ajax call to finish. In capybara 2.0.0, wait_until method is removed as it is not needed and was creating confusion. Capybara automatically waits for elements to appear or disappear on the page. If you do something like find("#foo"), this will block until an element with id “foo” appears on the page, as will has_content?("bar"), click_link("baz") and most other things you can do with Capybara.

Since today morning I was facing very strange issue with capybara feature spec. One of my spec was failing with following error.
spec:
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_text("This is my comment")
      page.should have_link('Reply')
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end

       expected to find text "This is my comment" in "BROWSE CATEGORIES FORMATS MY PREGNANCY VOICES SERVICES POST TAGS MyText About Author1 — A brief write up about the author will come here. This will not exceed more than 250 characters. Give us your opinion. Discuss it with people like you. Javier Huel - This is my comment 0 likes | 0 replies LIKE | Reply | Edit | Destroy SIMILAR POSTS about us | our team | our vision | give feedback | privacy policy | terms of use | best viewed in | careers | site map © 1998–2013 ABC, Inc. All rights reserved."
     # ./spec/features/commets_spec.rb:42:in `block (3 levels) in ' 

Now in above error, I am clearly able to see that the text is present in page, but still it is not able to match it.

To quickly understand what is happening, I have used pry. On pry prompt, just verified expectation again and this time it is evaluated true.
[1] pry(#)> page.should have_text("This is my comment")
=> true 

Means, here capybara was not waiting for ajax call to finish.

So I have googled for "capybara + wait for ajax call to finish" and found solution to use "wait_until" method.

When I have tried this method, I got into another error,
Failure/Error: wait_until do
     NoMethodError:
       undefined method `wait_until' for #
     # ./spec/features/commets_spec.rb:41:in `block (3 levels) in '
While googled more, I found strong discussion of Jonas Nicklas and found that wait_until is removed from capybara 2.0.0 ;(.

Alternatively, I found how to implement "wait_until" back into capybara.

But in same discussion Jonas Nicklas has posted a link of his blog post where he has explained about why this decision was reached. Also he has mentioned how we can wait for ajax call to modify DOM.

So, my spec changed to,
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_selector(".comment", :text => "This is my comment")
      page.should have_link('Reply')                         # Subsequent call also passed due to have_selector
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end
 
and which is passing. ;)

References :
https://groups.google.com/forum/#!topic/ruby-capybara/qQYWpQb9FzY
http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara
https://gist.github.com/KevinTriplett/5087744

Monday, May 6, 2013

Ubuntu 13.04 + XPS Dell + sound is not working

Recently I have upgraded my Ubuntu OS from 12.10 to 13.04 on my Dell XPS.

But suddenly I faced issue with my sound. I am not able to get sound form my machine.

After googling, I found solution for Dell Vostro, but after some time I found for dell xps too. I tried solution on my Dell box and my sound is back.

Here is the solution,
sudo gedit /etc/modprobe.d/alsa-base.conf
Add following line at the bottom of a file
options snd-hda-intel model=dell-bios
But don't forget to reboot your machine.
References :
http://askubuntu.com/questions/288503/vostro-3560-sound-is-not-working-after-upgrade-to-ubuntu-13-04
http://ubuntuforums.org/showthread.php?t=1457676

Saturday, September 22, 2012

Rails 4 - Transaction isolation level


After long time I am writing small post, as now a days not able manage a time or you can say I am just working ;)

TL;DR - In Rails 4, Jon Leighton has added a support for specifying transaction isolation level. This transaction level we can pass when starting transaction(or you can say while calling transaction method)

Note : This blog post is copied from Jon Leighton commits, which he did in a rails repository

Before continuing reading this post, it will be good if you read about Transaction Isolation over here.

Isolation is a property that defines how/when the changes made by one operation become visible to other concurrent operations. Isolation is one of the ACID (Atomicity, Consistency, Isolation, Durability) properties.

Highlevel of Isolation does affect on Concurrency property of ACID, as it require to use locks or multiversion concurrency control.

Anyways here is a documentation which Jon Leighton has added in rails trank.

If your database supports setting the isolation level for a transaction, you can set it like so:

Post.transaction(isolation: :serializable) do
  # ...
end

Valid isolation levels are:
* `:read_uncommitted`
* `:read_committed`
* `:repeatable_read`
* `:serializable`

You should consult the documentation for your database to understand the semantics of these different levels:

* http://www.postgresql.org/docs/9.1/static/transaction-iso.html
* https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html

An `ActiveRecord::TransactionIsolationError` will be raised if:
* The adapter does not support setting the isolation level
* You are joining an existing open transaction
* You are creating a nested (savepoint) transaction

The mysql, mysql2 and postgresql adapters support setting the transaction isolation level. However, support is disabled for mysql versions below 5, because they are affected by a bug (http://bugs.mysql.com/bug.php?id=39170) which means the isolation level gets persisted outside the transaction.
References :
http://en.wikipedia.org/wiki/Isolation_(database_systems)
http://www.postgresql.org/docs/9.1/static/transaction-iso.html
https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
https://github.com/rails/rails/commit/392eeecc11a291e406db927a18b75f41b2658253

Sunday, July 1, 2012

bundle install +
tar_input.rb:49:in `initialize': not in gzip format (Zlib::GzipFile::Error)

This is one of the pending post since 6 month, thought it might helpful to some people.

Today on production server our system team found very strange issue with REE and bundler which lead production server was down for 20 min. This issue did not occur on test (Since on test and production have different version of REE)

Issue is as follows,


Issue was new to me, so I started digging into it using the almighty google.

The solution which I found on net was not applicable since we couldn't afford a downtime.

Issue is actually with ruby-enterprise-1.8.7-2010.02. Which is fixed in ruby-enterprise-1.8.7-2011.01 as described in release note.

http://blog.phusion.nl/2011/02/12/ruby-enterprise-edition-1-8-7-2011-01-released/

But we don't have a time to upgrade REE at this point. So I digged into it and found temporary solution described here.

http://stackoverflow.com/questions/2494659/strange-bundler-error-tar-input-rb49in-initialize-not-in-gzip-format-zlib


Basically you have to clear you gem cache either you are use rvm or not. so I have simply deleted content from my gems folder
like,
sudo rm -rf /opt/ruby-enterprise-1.8.7-2010.02/lib/ruby/gems/1.8/cache/

And this worked !!!....