On my current project I had to calculate date of Wednesday in nth week in mth year.
Here is the trick:
date-extension
Following my previous blog, I happen to work on another rails upgrade at the beginning of my new project. This time rails 3.0.7 to 3.1. I though, I should document these steps, as it might help myself or someone else in future.
I googled and found a good discussion on
stack-overflow. As
Forrest Ye gave the right answer, I began the migration step by step, as it is written:
"Edit Gemfile, change Rails gem version
gem 'rails', '3.1.0'
Also adds new gems introduced in 3.1.0
group :assets do
gem 'sass-rails', "~> 3.1.0"
gem 'coffee-rails', "~> 3.1.0"
gem 'uglifier'
end
gem 'jquery-rails'
run
bundle update rails
Then run rake rails:update and resolve conflicts."
I found few conflicts and before accepting them, I tried to understand:
1. config/application.rb:
I paid attention to:
config.assets.enabled = true
config.assets.version = '1.0'
which means you are enabling assets pipe-lining which is one of the feature of rails 3.1, I accepted the changes.
2. config/environments/development.rb:
config.action_view.debug_rjs = true
which has been deprecated, so this line should be deleted, if you are using rjs in views, debugging is not supported now.
config.assets.compress = false
which means assets compression is off in development, I accepted the changes.
config.assets.debug = true
it means, you can see the assets being loaded in logs. I accepted this change.
3. config/environments/production.rb
I saw again configuration related to asset pipeline:
config.assets.compress = true
config.assets.compile = false
config.assets.digest = true
Simply, accept these changes.
Once this is done, move all assets from public/images, public/javascripts, public/stylesheets to app/assets/ directory. Again, you have to make sure, that you
Include css/javascript links in your layout file like this
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
Again, it is important to understand the behavior of asset pipeline and how it works. For example, in application.js file, you will have:
that means, all the files from assets/javascripts directory will be loaded automatically, as application is being loaded, in hierarchical format. If you do need js files in sequence, mention that above this line, e.g.:
//= require jquery
//= require jquery_ujs
//= require highcharts
//= require_tree .
Similarly you have application.css file, which will load assets in hierarchy, so override it if needed.
One important thing I noticed is about images, which are embedded in stylesheets. I had stylesheet.css as:
/*
*= require_self
*= require_tree .
*/
#header { height:20px; }
body {background-image:url(../images/mainBack.png) !important; background-repeat:repeat-x;}
So, after moving images to app/assets/images directory, I have to remove the relative path of the image:
body {background-image:url(mainBack.png) !important; background-repeat:repeat-x;}
Similarly for all other styles and that is it!
Few weeks back I did rails upgrade for my current project from rails 2.3.5 to rails 3.0.9 (on Windows 7).
I began with
rails 3 upgrade guide. So, first step was to install ruby 187, which I already had on my system with
pikThere is a good series on rails on upgrading to rails 3 from Ryan Bates:
http://asciicasts.com/episodes/226-upgrading-to-rails-3-part-1
http://asciicasts.com/episodes/226-upgrading-to-rails-3-part-2
http://asciicasts.com/episodes/226-upgrading-to-rails-3-part-3
Some how the
rails-upgrade-plugin did not work for me (as I was working on Windows 7) .
I then followed
the-path-to-rails-3-approaching-the-upgrade step-by-step and I got application 80% upgraded, the rest 20% was a tough part.
In this blog, I am writing about challenges I faced in this process.
- Installing rails 3.0.9 gem:
Somehow gem install rails shown errors:
C:\myapp>gem install rails
Successfully installed rails-3.0.9
1 gem installed
Installing ri documentation for rails-3.0.9...
file 'lib' not found
Installing RDoc documentation for rails-3.0.9...
file 'lib' not found
I used,
C:\myapp>gem install rails --no-rdoc --no-riSuccessfully installed rails-3.0.9
1 gem installed
- routes.rb
I had to update the routes.rb file and I found one really helpful link: http://stackoverflow.com/questions/3103765/routing-in-rails-3-map-with-options
- Deprecation warning for cookie_store:
DEPRECATION WARNING: ActionController::Base.session= is deprecated. Please configure it on your application with config.session_store :cookie_store, :key => '....'. (called from C:/myapp/config/initializers/session_store.rb:7)
So, I did:
module Myapp class Application < Rails::Application config.session_store :cookie_store, :key => '_myapp_session', :secret => 'some-secret-string-very-long'
Also, I removed C:/myapp/config/initializers/session_store.rb file.
Reference: http://stackoverflow.com/questions/3720379/sqlsessionstore-in-rails-3 and http://apidock.com/rails/ActiveRecord/SessionStore
- Logging the deprecation warnings
I saw another warning:
You did not specify how you would like Rails to report deprecation notices for your development environment, please set config.active_support.deprecation to :log at config/environments/development.rb
So, I updated development.rb (and other .rb files):
Myapp::Application.configure do config.active_support.deprecation = :log - consider_all_requests_local warning
DEPRECATION WARNING: ActionController::Base.consider_all_requests_local= is deprecated. Please configure it on your application with config.consider_all_requests_local=.
So, I added this snippet in config/environments/development.rb
Myapp::Application.configure do config.consider_all_requests_local = true - Some easy once
1. DEPRECATION WARNING: RAILS_ENV is deprecated. Please use ::Rails.env.
So, wherever I was using RAILS_ENV, I replaced with Rails.env
2. DEPRECATION WARNING: RAILS_ROOT is deprecated. Please use ::Rails.root.to_s.
So, wherever I was using RAILS_ROOT, I replaced with Rails.root.to_s
- ActionMailer warnings
1. When you need to set character set for action mailer, you specify in initializer.rb, which throws this warning:
DEPRECATION WARNING: ActionMailer::Base.default_charset=value is deprecated, use default :charset => value instead
I passed hash to the default method in application.rb:
Myapp::Application.configure do default :charset => 'utf-8'
2 Similarly the mime version we set in initializers:
DEPRECATION WARNING: ActionMailer::Base.default_mime_version=value is deprecated, use default :mime_version => value instead.
I passed hash to the default method in application.rb:
Myapp::Application.configure do default :mime_version => '1.0'
3. For multipart emails the setting has to be adjusted:
DEPRECATION WARNING: ActionMailer::Base.default_implicit_parts_order=value is deprecated, use default :implicit_parts_order => value instead
This is again can be passed to default method:
Myapp::Application.configure do default :parts_order => [ "text/plain", "text/enriched", "text/html" ]
- html_safe in view?
For 2-3 hours I struggled to find the reason, why the select tag was not producing the tags. When I found it I understand that this is again a security addition to convert strings into html safe form. So, all your '<' will be converted to '<' and all '>' will be converted into '>'. I actually added .html_safe at the end of each string in view and that showed all the tags finally.
Reference: rails-3-select-tag-not-producing-dom-elements
- Removed config.action_view.cache_template_loading, use config.cache_classes instead
Reference: https://github.com/rails/rails/commit/83e29b9773ac113ceacb1e36c2f333d692de2573
- Rspec
I was using rspec and here is the deprecation warning I got:
*****************************************************************
DEPRECATION WARNING: you are using a deprecated constant that will be removed from a future version of RSpec.
C:/cit/spec/controllers/activity_logs_controller_spec.rb:1:in `require'
* Spec is deprecated.
* RSpec is the new top-level module in RSpec-2
***************************************************************
***************************************************************
DEPRECATION WARNING: you are using deprecated behaviour that will be removed from a future version of RSpec.
C:/cit/spec/spec_helper.rb:17
* Spec::Runner.configure is deprecated.
* please use RSpec.configure instead.
So, I have to use rspec-rails-2 which supports rails 3.
Reference: http://groups.google.com/group/cukes/browse_thread/thread/40c4c2aea1c07afe/cb63eb7f0b733646?lnk=raot
- Ajax stopped working
I was using jQuery as javascript library, and I found somehow the ajax calls stopped working after the upgrade. I later found that there is a security threat and a fix for that is available
Here is what I had to do:
Step#1. In application.html.erb, I had to include csrf_meta_tag:
<%= javascript_include_tag :defaults %><%= csrf_meta_tag %>
Step#2. I added this snippet to application.js:
$(document).ajaxSend(function(e, xhr, options) { var token = $("meta[name='csrf-token']").attr("content");
xhr.setRequestHeader("X-CSRF-Token", token);});
Reference: csrf-protection-bypass-in-ruby-on-rails
- RackBaseURI instead of RailsBaseURI
I was using RailsBaseURI in apache, but after upgrade to Rails 3, I had to update it to RackBaseURI
Reference: RackBaseURI
- Last but not least, I was using valid? method in helper to check if the object is valid or not and based on that I was displaying certain fields, which was working fine in Rails 2. In Rails 3 valid? method will trigger validations as well, which caused field_with_errors div to show-up all the time (even when the validation should not happen). I changed the logic to not use valid? method to display other fields and it solved the problem.
In my previous
post, I set-up bundler with rails-2.2.2 application. Soon after that, I found myself in another trouble.
When I set-up bundler, I had to create
Gemfile for the gems required by my rails application. Also, when I run
bundle install command, it creates
Gemfile.lock file, this file holds various information:
- Gem repositories (sources)
- Which version of gem the application is using (gem specification)
- If there is any dependency of any gem, then what they are
- Platform details
In my current project, I use Windows 7 as development machine, there is a
cruise server on linux machine, I am committing my code to a
subversion repository and once the build passes on cruise it deploys to a linux server. Every time I run bundle install, it creates a Gemfile.lock with windows version of gems and platform details for windows. If I commit Gemfile and Gemfile.lock, it fails the build and I have to manually login to cruise box and remove those files and re-run bundle install command, so that it generates the Gemfile and Gemfile.lock for linux environment, so that on integration and production servers, the Gemfile and Gemfile.lock will remain with linux version and there is no failures there. This was a pain.
- I extended cruise:init rake task(created fix.rake in lib), to include my ruby script.
- In the script, I am removing windows traces, updating the platform details, running bundle command and then committing the latest Gemfile and Gemgfile.lock toSVN.
This way, if by mistake I am committing Gemfile and Gemfile.lock to SVN, I don't need to go to cruise and re-generate Gemfile and Gemfile.lock.
I am working on a rails application, which is on rails-2.2.2. I have used bundler on rails-3 application, but never tried bundler for rails-2.2.2. The benefit of bundler is huge, it is gem manager for rails application. So, now no pain of vendoring/localizing gems in rails application.
gembundler.com gives step-by-step process on how to install/configure bundler for rails-2.3.x and rails-3.x applications. So, I wanted to find out how does it work with rails-2.2.2 application.
So, here is how I did.
- First of all I have installed bundler
gem install bundler // installed bundler 1.0.10 - Updated rubygems
gem update --system // rubygems > 1.3.6 - Created config/preinitializer.rb file with below content:
begin
require "rubygems"
require "bundler"
rescue LoadError
raise "Could not load the bundler gem. Install it with `gem install bundler`."
end
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.24")
raise RuntimeError, "Your bundler version is too old for Rails 2.3." +
"Run `gem install bundler` to upgrade."
end
begin
# Set up load paths for all bundled gems
ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
Bundler.setup
rescue Bundler::GemNotFound
raise RuntimeError, "Bundler couldn't find some gems." +
"Did you run `bundle install`?"
end
- Created Gemfile with something similar:
require 'rubygems'
source 'http://rubygems.org'
source 'http://gemcutter.org'
source 'http://gems.github.com'
gem 'rails', '2.2.2'
gem 'hpricot', '0.6.0'
gem "composite_primary_keys", '1.0.8'
gem 'javan-whenever', '0.3.7'
gem 'mysql', '2.8.1'
gem 'activerecord-oracle_enhanced-adapter', '1.1.9'
gem 'ruby-oci8', '1.0.3'
group :test do
gem 'mocha', '0.9.5'
gem 'rspec', '1.3.1', :require => 'spec'
gem 'rspec-rails', '1.3.3'
gem 'database_cleaner', '0.5.0'
gem 'capybara'
gem 'ruby-debug'
gem 'factory_girl'
end - From the application root, I ran:
bundle install
That is it and I have my application up and running with bundler. Once I tested my application, I removed gems folder from
/vendor (no need for localized version of gems now.)
Few days back, I did introspection and thought, as a developer:
- How many languages I know?
- How many databases I have worked with?
- Which frameworks I am comfortable working with?
- Do I know the latest technologies in my field?
I then realized that working on PHP 4, PHP 5, Ruby 1.8.6 doesn't mean that I know the latest cutting edge technologies. I should try learning may be Ruby 1.9.2, or may be a new language. I have worked with MySQL, Oracle, but what's new? I should try working with some other database, may be Mongodb. I did work on Drupal 4.7.x, 5.7.x and also on Rails 2.0.x to 2.3.x, but may be I should try Rails 3.
Then I downloaded Ruby 1.9.2, with
pik, so I can continue work on the project work(ruby 1.8.6), as well as try out rails 3. Needless to say Rails 3.x is far better than it's predecessor Rails 2.x.
bundler is just awesome! But I was so used to use ActiveRecord (with mysql and oracle), I was not able to think how would I work without it on a Rails project.
I first tried
mongodb online (Try it out section) and felt it different, but complete. Whatever basic operations you can perform in mysql/oracle, you can do it in that tutorial.
Once I was done with my rails3-mongodb test application, I realized that MySQL and Oracle are not end of the world. There are better and different things that I should learn. It is much faster to install and configure Ruby 1.9.2 + mongodb + rails 3 and get the application up and running as compared to Ruby 1.9.2 + MySQL/OracleXE (10g) + rails 3 from scratch.
I would love to hear comments/similar experiences :)
‘High Performance Team’, a phrase that one always dreams to be a part of some day. A team that inspires other teams to achieve what they would generally consider way above their potential. A team, where there is no place for finger pointing, rut and panic(even in crucial times). I am working in a team which is high performing and I would like to uncover the challenges we faced and how we overcome those as a team.
We never thought that one day we will be a successful and high performing team nor did it happen overnight. We faced the moments of finger pointing, failures, boredom and heated discussions during the initial phase. We slowly made use of continuous ‘retrospection’ from ‘Scrum’, which helped a lot. We found that retrospection never hurts, even if you don’t have any failure since last retrospection, you may find better ways of doing the same thing. We saw that rhythm is important, as repetition reduces complexity. So, we tried continuous improvement in everything, be it very small discussion, a big deployment or regular review meeting with product owners and sponsors. Soon, we found our key mantra, ‘rhythm’ and we stick to it.
Challenges:I believe most of the teams face these challenges, but since our team was very small(one developer, one hands-on architect and one product owner) and our sprints were small (one week), these became crucial for various reasons:
* Begin meetings on schedule: Since we have meetings with different groups and most of the times they are back to back, it became crucial to join meeting on time.
* Meetings, never end on time: This costs a lot if you have small development cycle, if meetings go beyond their scheduled time, you have to back fill the development hours, may be sometimes by staying late and sometimes working over the weekend to avoid delayed deliveries.
* Distractions from the agenda: This one is real killer and one of the real cause of the previous challenge. It happened very often in our team, until we found solution for this.
* No more boring stories (tasks): How would you feel if you are given to write automated functional test cases for more than 50 combinative scenarios in a sprint?
* New Ideas/innovation: Every team feels that it should innovate something or implement new ideas, that no one around have thought about (which in turn brings confidence and enthusiasm)
* Effective retrospection: Every Scrum project team does the retrospection, but how can we do it in a better way was a big challenge, as we did not want to do it as a ritual which will die in few days.
* Avoid panicking in crucial times: I admit, no one can avoid panicking in a production failure, but we all know it will not help either. The challenge was to learn how to avoid panic in crucial moments and keep the calm hat on, so that team can find right solution in instead of a quick and dirty solution.
* Why failures in QA instead of development environment?: No matter how much we test some issues come-up in QA instead of development, which is a big challenge for any team.
* Demanding stakeholders do not agree for automation testing: It was always challenging to convince sponsors for automated unit/functional/integration testing. Their question was dead straight, “Why would I spend my money on something which is not a must have feature in my product?”
Approach to solve these challenges:It all started with retrospection and I love saying that retrospection is the best process Scrum has come-up with. It is a forum to brainstorm, rather than asking questions like, why you have done that crap, why you did not build the right code? Who love such questions? I don’t and I don’t think anybody would love it. There are better ways to do the retrospection and I think our team came-up with the right approach.
* Effective retrospection: We all do the retrospection in Scrum projects, sometimes every sprint, sometimes on ad-hoc basis. We were doing retrospection, but in three to four weeks, it became boring, we were not getting benefit of the retrospection. Then we tried new approach, we started maintaining ‘retrospection-backlog’. We took ownership of say two items per person per sprint. Until the team agree, that we have worked on those items, we do not close (or mark as done) those items in the backlog. Also, we made those items appear in the stand-up, as whether we have acted on those items or not. Now, we are not addressing them at the end of the sprint, but we were addressing them daily. Result? We close many items in the backlog and suddenly it is working for us. There are many of the retrospection items I have listed in this paper, one I liked most, “Cutting corners never help in a big picture. Shortcuts work for few days, but you have to pay it’s cost in a longer run”.
* Begin meetings on schedule: A big challenge, I feel it is very easy to say, that we begin on time, but difficult to follow. We spoke in our retrospective and decided to maintain a backlog on our own, and whoever is late (more than two minutes) three times should give a treat to the team or bring snacks items (we maintain a snacks corner in our team room) or bring small logical game (we have a game zone too!). It really worked!
* Meetings, never end on time: Another challenge and another retrospective was waiting for it. One of us volunteered to do time checks in meeting, so that participants talk precisely what is important and it helped to finish meetings on time.
* Distractions from the meeting: This one was one of the root cause of previous challenge and we came up with parking lot idea. It is really helpful, if you see someone is distracting the discussion and one of us would raise a hand, which means: ‘Focus!’.
* No more boring stories (tasks): This happened with me, when I was asked to write automated functional test cases for more than 50 combinatorial scenarios in a sprint. It is boring to the death at the same time you have to keep reminding yourself that you are not missing any crucial scenario, otherwise needless to say your efforts are waste. I discussed with my product owner and we came-up with a mutually exclusive and collectively exhaustive(MECE) list of scenarios on ‘matrix based problem solving’ approach.

It was new for me and in less than one hour we discussed only important scenarios and implemented them, which kept the problem interesting at the same time it helped us to achieve the goal.
* New Ideas/innovation: We as a team would like to try out different things, but the pressure of development work never let us do ideas, which we wanted to try-out (for example unit testing of javascript code). We made a retrospective item that as a team we will spend some time to try-out new things, sync-up with the new technologies/we trends. The ‘so what’ for our product owner was, better solution will come upfront when the team, a) learn new things and b) share the ideas/innovations with broader groups. Since we added this as a retrospection item and even product owner voted on that, we found no excuses and started implementing new ideas. Another idea we came-up with was to have backlog for ‘Most Teamish Room goal’ and the acceptance criteria was, if anyone stop by, ‘wow!’ should be the first expression/reaction.
* Books are best friends, how can they help my team?: Building a team with collaboration and learning, Do it yourself, workaholics is killer, good enough is fine, pick a fight and how to say you’re sorry, are few of the principles I have learnt from few books I have read. Books not only guide you, but more importantly they keep hammering the concepts, so that you are bound to find a better way of doing the same thing.
* Avoid panicking in crucial times: It happened when ‘we’ as a team tasted failure first time. Again, retrospection came to rescue us. Once we got over the failure, we did the retrospection from a distance (after a week) and found that instead of panicking, it was our cool mind, which helped us. We were very tempted to go with quick and dirty solution, but we waited and thought, ‘Is this the right solution for the long term?’. It helped and this approach became our habit. The key is to, ‘never lose the calmness, never in a crucial situation’.
* Why failures in QA instead of development environment?: We happened to see failures in QA, even when developers do their part of testing in development environment. In our retrospection we brought this and came-up with, ‘Team testing time with freinds’. So, we called few friends who were neither part of development team nor part of sponsors’ group. This strategy worked and we started catching issue in development environment.
* Demanding sponsors do not agree for automation testing We find it difficult to convince sponsors for tech debt or automated testing stories. One of the retrospection item was to present a Scrum factoid at the end of the sprint review meeting, to help sponsors understand the Scrum more. It worked like a charm. More we involved sponsors (though they hesitated initially), more it became easier to convince to them what development team wanted to try to improve the code quality(and obviously to improve the product).
Benefits:With the challenges we faced, we uncovered a lot many solutions for variety of problems. Be it technical problem or a process issue, we decided not to be caught up in a rut of work. The benefits were many folded.
Joy of work and flow of energy: Most importantly team was enjoying the work. No task or deadline pushed us to do something, everything was in flow.
Confidence of stakeholders: We won the confidence of the sponsors and product owners.
Software Craftsmanship: We implemented our own ideas (software craftsmanship), which boosted morale and confidence of the team.
No more ‘staying late’ nightmaresRetrospect everything: Retrospectives made life much easy. Nothing fell of the cracks, as we were doing retrospection every week.
Celebrations: Team had a lot to celebrate; their innovation, problem solving approach and knowledge sharing with other teams. Best practices became part of the team activities.
In all it became a win-win situation for everyone.
I feel that every team can become a ‘High Performing Team’. It only takes small efforts every sprint, but in a longer run, you will find yourself out of those nightmares of teams with lack of energy, lack of vision and above all lack of self confidence. Retrospection is the best tool Scrum has given and each team will get benefit, if it is used. Finally, if you have the idea implement it, because, world needs execution of ideas (I think I read it in some book, ‘no one likes the idea guy!’).
I would like to thank my team members, with whom I lived this experience. Also to thank my colleagues and my seniors to inspire me to learn and achieve, they are not directly involved in project activities directly, but always helped with their continuous feedback.
References: 1. Rework - http://37signals.com/rework/
2. Beautiful Teams -Inspiring and Cautionary Tales from Veteran Team Leaders http://oreilly.com/catalog/9780596518028
In my
previous post I talked about adding size and stylesheets to an inline editable field with
jEditable.
I have this div:
<div >
<div class="mouseover"> Title </div>
<div class="help_text"> (Click on the Title to edit) </div>
</div >
I had to add some text next to this field which says "click to edit". When a user clicks on it , I wanted to add a callback function to this field, so that when I click on it title comes in input tag as inline editable and the text in next div (Clieck on the Title to edit) should go away. I found that you can use
callback here just like in any
ajax call.
Here is what I did:
$(".mouseover").editable('<custom url>', {
indicator : '<custom image path>',
onblur : "submit",
placeholder : "click here to edit",
type : "custom_input",
callback : function(data, settings){
$(this).siblings(".help_text").text("(Click on the label to edit)");
}
});
(posting it ... so that someone else should not spend time in figuring out :)
I am using
jEditable a jQuery plug-in and is is really cool for inline editing. I stuck when I had to add size(say 50 characters) field and css for an input field which is inline editable.
Then, I extended jeditable and added my own input type with size and class attributes:
In anything.js:$(document).ready(function(){
jQuery.editable.addInputType('
custom_input', {
element : function(settings, original) {
var input = $('<input
size=50 class="input_inline"/>');
if (settings.width != 'none') { input.attr('width', settings.width); }
if (settings.height != 'none') { input.attr('height', settings.height); }
/* https://bugzilla.mozilla.org/show_bug.cgi?id=236791 */
//input[0].setAttribute('autocomplete','off');
input.attr('autocomplete','off');
$(this).append(input);
return(input);
}
});
});
$(".mouseover").editable('<custom url>', {
indicator : '<custom image path>',
onblur : "submit",
placeholder : "click here to edit",
type : "
custom_input"
});
In any.html:<div class="mouseover"></div>
so all div with
mouseover class will become inline editable.
I recently got a requirement to generate MS Excel workbook with different worksheets in Ruby on Rails. Also, I had to give style to the column headers, set background color for table of content and make columns justified.
I came across
http://rubyonwindows.blogspot.com and
spreadsheet links which are very helpful if you want to try anything with MS Excel in Ruby.
I installed spreadsheet gem (
sudo gem install spreadsheet), but giving styles did not seem very straightforward
So, I thought of giving it a shot. Let's first try to create a workbook with three worksheets.
workbook = Spreadsheet::Workbook.newworksheet_for_books = workbook.create_worksheet(:name => "Books")worksheet_for_stationaries = workbook.create_worksheet(:name => "Stationary")worksheet_for_laptops = workbook.create_worksheet(:name => "Laptops")Now, set headers in all worksheets:
for worksheet in workbook.worksheets row = 0 (0..2).each do |column| worksheet[row, column] = "Name_#{column}" endendIn the above code snippet, you might have noted that:
- Iterating over worksheet is very simple, just like array.
- Setting value to a cell is very simple, just worksheet[row, column] = value
Now let's make the column headers bold and justified:
format_header = Spreadsheet::Format.new(:weight => :bold, :align => :justify)for worksheet in workbook.worksheets (0..4).each do |column| worksheet.row(0).set_format(column, format_header) endend
Now let's try to add background color 'yellow' to these headers:
format_header = Spreadsheet::Format.new(:weight => :bold, :align => :justify, :pattern_fg_color => :yellow)
for worksheet in workbook.worksheets
(0..4).each do |column|
worksheet.row(0).set_format(column, format_header)
end
end
It didn't work! why? no it's not because we have added :pattern_fg_color , you might be thinking :pattern_bg_color is for background, but this is wrong, this is what I found in the references.
"Use :pattern_fg_color to set background of a cell"
Anyways, even this option didn't work for me. I couldn't understand why. But I found a work around(
reference) and here is the enhanced version:
Create color_format class and put it in a library
class ColorFormat < Spreadsheet::Format
def initialize(opts)
super opts
end
end
And our code will look like:
require "color_format"
format_header = ColorFormat.new(:weight => :bold, :align => :justify, :pattern_fg_color => :yellow, :pattern => 1)
for worksheet in workbook.worksheets
(0..4).each do |column|
worksheet.row(0).set_format(column, format_header)
end
end
And that's it!
I stumbled across a situation when I have to schedule a job and I have to store today's weekday. I am also storing the schedule's created_at/ updated_at time in datetime format.
To store today's weekday, I said:
weekday = Time.now.wday
>> 4 #Thursday
When I stored the record
Schedule.first.created_at.wday
>> 3 #Wednesday
Why?
I dug in more and here are the facts.
We so say in environment.rb file:
config.time_zone = 'UTC'
But this applies to only the created_at/updated_at attributes and sets up the default timezone for the database and never applies to Time.now
So to keep Time.now in sync we have to set ENV['TZ'] in environment.rb file.
Again in console:
weekday = Time.now.wday
>> 3 #Wednesday
Schedule.first.created_at.wday
>> 3 #Wednesday
:)
I think I have never encountered this issue until today. On my current Ruby on Rails project I am using Oracle as the database. I have a user model, which has an attribute email and the requirement was that a user can have more than one email id (separated by comma). I initially thought that I should use :text type, as I literally forgot that I am writing a migration against oracle database. Later, when the actual requirement come into the picture, I had to revise as user can have just one email-id and I started thinking about changing the type from :text to string.
There were two reasons to change the column type:
1. As per the requirement, user cannot have so many email-ids
2. Changing the column type from clob to string in oracle would give good performance boost
I decided to change the column type by simply using :
change_column :users, :email, :string, :limit => 4000
D:\rubyapp>rake db:migrate
(in D:/rubyapp)
== ChangeEmailColumnTypeToStringInUsers: migrating ===========================
-- change_column(:users, :email, :string, {:limit=>4000})
rake aborted!
An error has occurred, all later migrations canceled:
OCIError: ORA-22859: invalid modification of columns: ALTER TABLE users MODIFY email VARCHAR2(4000)
(See full trace by running task with --trace)
Failed! But why?
I came across:
http://snippets.dzone.com/posts/show/3022I followed the same pattern:
1. Add a temporary column with type as string (varchar2 in local)
2. Update all the records and copy text from email column to temporary column
3. Remove email column
4. Rename temporary column to email
I decided to not to run oracle commands in migration.
Here is how my migration now looks like:
class ChangeEmailColumnTypeToStringInUsers < ActiveRecord::Migration
def self.up
add_column :users, :email_temp, :string, :limit => 200
User.all.each do |user|
user.update_attribute("email_temp", user.email)
end
remove_column :users, :email
rename_column :users, :email_temp, :email
end
def self.down
add_column :users, :email_temp, :text
User.all.each do |user|
user.update_attribute("email_temp", user.email)
end
remove_column :users, :email
rename_column :users, :email_temp, :email
end
end
Very simple!
I was looking for a ruby method in my Ruby on Rails project which can truncate a string, say after 50 characters.
I found 'truncate' method, which is a rails api method for truncating string in views.
So if have string
str = "Hi, this is Gourav Tiwari, how are you??" # total 40 characters
the truncate method can be used in the ciew like this:
truncate(str, :length => 25, :ommision => " - - -")
and the result would be :
"Hi, this is Gourav Tiwari, - - -"
In ruby I can accomplish same by slice and concatenation:
myproject>ruby script\console
>> str = "Hi, this is Gourav Tiwari, how are you??"
=> "Hi, this is Gourav Tiwari, how are you??"
>> str.slice(0..25)
=> "Hi, this is Gourav Tiwari,"
>> str.slice(0..25) + " - - -"
=> "Hi, this is Gourav Tiwari, - - -"
Very simple!
Playing more on JQgrid plugin from 2dconcept, I faced a strange issue. Sometimes it skips some pages.
For example if you have total 15 pages, and you navigate till 6th page and then you hit next page button on the grid, it will not show you the 7th page! you have to click next page button once more to see the 8th page. So where the 7th page is gone?
No, it's not invisible, it's all hidden in json response.
When I saw the response for page 7 (using firbug), it was coming well, but the grid was not showing the 7th page at all. I dug into the library file more and figured out that, if in the response you have a double quote, it will not display that page.
Say you have this json response:
{"page":"1","total":1,"records":"1","rows":[{"id":"7","cell":["gourav tiwari hel"lo!","this","should","be right!"]}]}
See the double quote in the response.
I tried using to_json library method, but it actually removes all the double quotes, even the necessary ones as well. So not a good idea. I extended to_jqgrid_json method like this:
module JqgridJson
def to_jqgrid_json(attributes, current_page, per_page, total)
json = %Q({"page":"#{current_page}","total":#{total/per_page.to_i+1},"records":"#{total}")
if total > 0
json << %Q(,"rows":[)
each do |elem|
elem.id ||= index(elem)
json << %Q({"id":"#{elem.id}","cell":[)
couples = elem.attributes.symbolize_keys
attributes.each do |atr|
value = get_atr_value(elem, atr, couples)
value = value.is_a?(String) ? value.gsub(/"/, '\"') : value # added this line
json << %Q("#{value}",)
end
json.chop! << "]},"
end
json.chop! << "]}"
else
json << "}"
end
end
end
And the response become this:
{"page":"1","total":1,"records":"1","rows":[{"id":"7","cell":["gourav tiwari hel\"lo!","this","should","be right!"]}]}
No invisible page in JQGrid anymore!
In one of my earlier projects, there was a requirement from product owners, to show data in grid format. It was way back in summar of 2008. We had to load the whole data by hand-coding the trs and tds in table and shown the results in html table's grid format.
Back to the current project, I did a little research and found jquery-grid-rails-plugin
I found it pretty useful and up to the mark as far as results are being displayed on the Grid (AJAX calls). You can look at the Demo as well (http://github.com/ahe/jqgrid_demo_app/tree/master), also a detailed explanation on installation and usage mentioned here -> http://www.2dconcept.com/jquery-grid-rails-plugin
The response is pretty quick, from 1000 records in DB to fetch 25 on one page it took around 500 ms. Also, you can use various JQuery themes if you do not like the default look and feel: http://jqueryui.com/themeroller
Lets take a very small example and see how to use JQgrid:
I have User controller's index method to write logic to display all users.
User model has attributes as:
id : integer (row id)
name : string
created_at : date
I have User's view (users/index.html.erb file) to display the grid with above attributes.
Steps:
1. Install plugin: $ ./script/plugin install git://github.com/ahe/2dc_jqgrid.git
2. In layout include JS and CSS for grid:
<%= jqgrid_stylesheets %>
<%= jqgrid_javascripts %>
3. In User controller:
def index
users = User.find(:all) do
if params[:_search] == "true"
name =~ "%#{params[:name]}%" if params[:name].present?
created_at =~ "%#{params[:created_at]}%" if params[:created-at].present?
end
paginate :page => params[:page], :per_page => params[:rows]
order_by "#{params[:sidx]} #{params[:sord]}"
end
respond_to do |format|
format.html
format.json { render :json => users.to_jqgrid_json([:id, :name, :created_at], params[:page], params[:rows], users.total_entries) } # total entries will get User.all.size
end
end
4. In view:
<%= jqgrid("All users", "users", users_url, # my recommendation as putting /users sometimes does not work in all cases
[ { :field => "id", :label => "ID", :width => 35, :resizable => false },
{ :field => "name", :label => "User Name" },
{ :field => "created_at", :label => "Created on" }
]
) %>
Now everything is fine, it should work fine and show us the grid with name and created at time.
Problem that I faced: When you have to show any attribute of model with formatting , there was nothing mention on the blog, like I would like to Capitalize the name here in the grid (First letter is capital).
So, I dug into the code and found my solution
Solution:
In index I have to change the format.json in index method like this:
render :json => users.to_jqgrid_json([:id, "name.capitalize", :created_at], params[:page], params[:rows], users.total_entries)
and in view I need to change following line as :
{ :field => "name.capitalize", :label => "User Name" },
and it works!
Another problem: If I need to change the format of the created_at, can I do this in controller and view?
Controller:
render :json => users.to_jqgrid_json([:id, "name.capitalize", 'created_at.strftime("%Y-%m-%d")'], params[:page], params[:rows], users.total_entries)
View:
{ :field => "name.capitalize", :label => "User Name" },
{ :field => 'created_at.strftime("%Y-%m-%d")', :label => "Created on" }
Answer is NO.
Because, the way jqgrid ruby plugin written, you cannot use method with parameters.
Solution: I used instance method.
In model, I wrote:
def formatted_created_at
created_at.strftime("%Y-%m-%d")
end
def capitalized_name
name.capitalize
end
In controller:
render :json => users.to_jqgrid_json([:id, :capitalized_name, :formatted_created_at], params[:page], params[:rows], users.total_entries)
In view:
{ :field => "capitalized_name", :label => "User Name" },
{ :field => "formatted_created_at", :label => "Created on" }
That's it!
Cucumber is one of the cornerstone for BDD in Ruby on Rails. Non technical or business participants can write tests (called as features or stories) in plain English and cucumber allows the developers to execute these tests.
I was very excited when I started looking at it. In my test.feature file, I have a feature:
Feature: Grant and revoke access
To have proper security modelUser should have proper accessScenario: Revoke access of a user who do not deserves access Given Gourav is a user who do not deserves access When system run cron job to verify access Then Gourav's access should be revoked
There are two ways to execute this file:
1. Cucumber as a plugin (by rake task):
It starts eating 'a' and 'A' characters on windows:
Feture: Grnt nd revoke ccess # fetures/grnt_nd_revoke_ccess.feture
To hve proper security model
User should hve proper ccess
Scenrio: Revoke ccess of user who do not deserves ccess # fetures/test.feture:5
Given Gourv is user who do not deserves ccess # fetures/test.feture:6
When system run cron job to verify ccess # fetures/test.feture:9
Then Gourv's ccess should be revoked # fetures/test.feture:10
I went to open source community and worked little bit on issue #81 (first encounter with Opensource community :) ) and found troubleshooting for this issue. Now "rake features" works well and give proper output.
2. Cucumber as a gem:
cucumber features\test.feature It gives:
Feature: Grant and revoke access # features/test.feature
To have proper security model
User should have proper access
Scenario: Revoke access of a user who do not deserves accessC:/ruby/lib/ruby/gems/1.8/
gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:70:in length': undefined methodjlength' for Scenario ng (NoMethodError)
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:74:in `max_line_length'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/tree/scenario.rb:78:in `padding_length'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/formatters/pretty_formatter.rb:197:in `padding_spaces'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/formatters/pretty_formatter.rb:58:in `scenario_executing'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:15:in `__send__'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:15:in `method_missing'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:13:in `each'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/broadcaster.rb:13:in `method_missing'
... 12 levels...
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/../lib/cucumber/cli.rb:11:in `execute'
from C:/ruby/lib/ruby/gems/1.8/gems/cucumber-0.1.12/bin/cucumber:6
from C:/ruby/bin/cucumber:19:in `load'
from C:/ruby/bin/cucumber:19
I found a workaround for this:
In file C:\ruby\lib\ruby\gems\1.8\gems\cucumber-0.1.12\lib\cucumber\tree\Scenario.rb on line number 69, I have changed jlength to length:
@length ||= Cucumber.language['scenario'].length + 2 + (@name.nil? ? 0 : @name.length)
Similarly, in file C:\ruby\lib\ruby\gems\1.8\gems\cucumber-0.1.12\lib\cucumber\tree\step.rb on line number 22 I have changed jlength to length:
keyword.length + 1 + name.length
But for a better solution I am still working on issue #81
Recently, I joined a new project. Before I get started, my tech lead asked me to go through the code first and find complex parts of it, so that during development I will be careful about those areas.
I decided to flog it and I came across Ryan Davis's flog tool.
What is Flog?
It's a tool, which helps you in finding complex areas of your code. More complex code tells you the chances of more bugs. Flog has some smart algorithm which go to methods of a file and gives points to each command/statement of that method. Say, if..else has 5 points, variable declaration has 1 point etc. So if your methods scores more means they are more complex.
How to do it?
Since I am working on windows(don't ask me why :) ), it took me some time to find how to use this tool. After a little hassle I found it and here I am showing the same, just like A, B, C. if you know a better way to do it, please do add your suggestions and comments.
Step A: Install flog as a gem.
Open command prompt:
c:/>gem install flog
Step B: Go to your project folder.
c:\>cd my_project
Step C: Just flog it!!!
- To get all the flog options:
c:\my_project>flog -h
flog options dirs_or_files -a display all flog results, not top 60%
-h display help -I=path extend $LOAD_PATH with path
-s display total score only -v verbosely display progress and errors
- If you want to flog all controllers, following will flog all the *.rb files in your apps\controllers folder:
c:\my_project>flog app\controllers\*.rb
- To flog controllers, models, helpers and libraries, together:
c:\my project>flog app\controllers\*.rb app\models\*.rb app\helpers\*.rb lib\*.rb
Sample method and flog score:
class PagesController
def show
@page_title = @page.title
@page_mode = "show"
respond_to do |format|
format.html
format.atom { render :layout => false }
end
end
In my last project, I worked as a developer on "Ruby on Rails". The software methodology was Agile (Scrum). Scrum is an iterative incremental process of software development commonly used with agile software development).We particularly picked up 1 week iteration (which is called "1 week sprint").
But why 1 week sprint?
I was suspicious whether 1 week iteration would be successful. It works this way, end of every one week development team has to show running and production deliverable code and so on, until the final iteration, when client team(Product Owner(s)) says, that this is the product I want.
Advantage is, the client team have much closer look what functionality they want and they get, so they can ask development team to implement those functionality which are more of client's interest. Every week they can prioritize the items and development team work on high priority items only.
And how Ruby On Rails fit in this picture?
I did not realized this until the last 4-5 sprints, when all of sudden our client team said that they want to modify the core business logic. I thought, is it possible to implement the change when everything is developed and software is 90% ready? And then, we relied upon unit testing with Rspec (see Behavior-driven testing with RSpec) and functional testing by Rspec stories(see telling stories by Rspec).
With unit testing in place covering more than 85% code, we were assured that the changes will go smoothly. Rspec stories on the functional side, assured us that business logic would be as per the client team's need.
Learning & Best Practices:
In the retrospective of the project, I noted following things which can be useful for other projects:
- Even if teams are in different location(can be tried with different time zones if possible), they should reserve team hours and should be in conference call. It is not necessary that team members should talk during this call, they can work on some other stuff, but if someone call your name in the conference, and say, "Hey Gourav, could you change this text or could you take a look at business logic?", then Gourav should respond to that.
- Everyday team should meet for 15 mins, everyone should join that meeting, to say what I did yesterday and what is the plan for next day.
- Developers should show and explain the functionality they have developed to the client team. It's win-win for both. Developers will get nods and appreciation of their work, which is better for the motivation of team and for client team as well, to get quick demo of the functionality they get, to provide quick feedback to development team.
- Team gets speed when dedicated UI developer joins the team.
- Team should have dedicated tester, otherwise it puts more burden on BA(Business Analyst)