Logo

If you smell something stinking, flog it!!!

over 3 years ago | Gourav Tiwari: easy_software = Agile.find(ruby_on_rails)

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

Agile and Ruby On Rails - hand in hand?

over 3 years ago | Gourav Tiwari: easy_software = Agile.find(ruby_on_rails)

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)

Using Ruby for shell scripting

over 3 years ago | Alex Rothenberg: Common Sense Software

Someone came to me the other day with a problem. He had downloaded a directory containing a large number of files with spaces in their filenames and needed to get rid of the spaces so he could load them into the tool he was using. He asked me if I knew of a mass rename tool. I didn't know of such a tool but thought it wouldn't be too hard to write something simple.

First I thought of capturing the output of a find and using a text editor search-and-replace create a whole number of mv old_file new_file_without_spaces commands that could all be run. But then I would have to work with some editor's search-and-replace syntax.

Then I thought of writing a shell script. I started to think about find . -e ... but realized I always get confused about the -e syntax and using sed or awk.

Suddenly I realized I could do this with Ruby. About 10 minutes later I had this simple 3 line script, ran it and was done.


Dir[File.expand_path("#{~/file_with_spaces}/*")].uniq.sort.each do |file|
system "mv '#{file}' '#{file.gsub(' ', '_')}'"
end



Ruby is my new shell scripting language and I don't think I'm ever writing a bash shell script again!

Using Ruby for shell scripting

over 3 years ago | Alex Rothenberg: Common Sense Software

Someone came to me the other day with a problem. He had downloaded a directory containing a large number of files with spaces in their filenames and needed to get rid of the spaces so he could load them into the tool he was using. He asked me if I knew of a mass rename tool. I didn't know of such a tool but thought it wouldn't be too hard to write something simple.

First I thought of capturing the output of a find and using a text editor search-and-replace create a whole number of mv old_file new_file_without_spaces commands that could all be run. But then I would have to work with some editor's search-and-replace syntax.

Then I thought of writing a shell script. I started to think about find . -e ... but realized I always get confused about the -e syntax and using sed or awk.

Suddenly I realized I could do this with Ruby. About 10 minutes later I had this simple 3 line script, ran it and was done.


Dir[File.expand_path("#{~/file_with_spaces}/*")].uniq.sort.each do |file|
system "mv '#{file}' '#{file.gsub(' ', '_')}'"
end



Ruby is my new shell scripting language and I don't think I'm ever writing a bash shell script again!

Be Happy

over 3 years ago | Surbhi Bhati: Clean Desk, Jammed drawers

Be Happy.
That you have one more sunset to see
Be Happy
That you have one more day to Glee
Be Happy
That you have one more try to choose what not to mind
Be Happy
That you have one more moment to be kind
Be Happy
That you have one more day at the center of chaos
Be Happy
That you have one chance to listen to someone's boohoos
Be Happy
That the world is still running
Be Happy
That the birds are still singing
Be Happy
That the Miracles happen
Be happy
That the god listens
Be Happy
That the chances are still bright
Be happy
That the hope is still in sight

Undefining a constant in a rspec test

over 3 years ago | Alex Rothenberg: Common Sense Software

I want to share something I just figured out. I had to write some code that used a class from a plugin if it was there and but did not require the plugin. The problem I faced was how to test this.

My method looked like this

class User 
def lookup_additional_person_info
if defined? CommonServices::Person
person = CommonServices::Person.find_by_id(@person_id)
@address = person.address
else
@address = 'not available'
end
end
end

I knew how to define CommonServices::Person to test the positive case but what about the negative? I found out you can undefine a class by sending its parent :remove_constant

  it 'should default additional parameters if CommonServices::Person is not defined' do
defined?(CommonServices::Person).should be_nil
user = User.new(:person_id=123)
user.lookup_additional_person_info
user.address.should == 'not available'
end

it 'should not populate additional parameters if CommonServices::Person is not defined' do
begin
defined?(CommonServices::Person).should be_nil
module CommonServices
class Person
end
end
CommonServices::Person.expects(:find).with(123).returns(person=mock)
person.expects(:address).returns(address_from_service=mock)

user = User.new(:person_id=123)
user.lookup_additional_person_info
user.address.should == address_from_service
ensure
CommonServices.send(:remove_const, :Person)
end
end

Testing with RSpec stories

over 3 years ago | Alex Rothenberg: Common Sense Software

I recently completed a project where we used RSpec stories for our integration testing and wanted to share some of my experiences. Overall RSpec Stories are an incredibly powerful tool and have allowed us to take a huge step towards business analysts writing functional specifications (in the form of plain text stories) then allowing the developers to make them pass. Today I'd like to share how we setup our stories and some of the decisions we made along the way.

The first goal was to run the stories from rake so we created a rake task in lib/tasks/stories.rake to run the stories and fail if any of them fail - this was useful when running this from our cruise server.


task :all do
args = ENV['story_prefix'] || ''
stories_passed = system 'ruby', File.join(RAILS_ROOT, "stories", "run_with_all_steps.rb"), args
raise('story failed') unless stories_passed
end


You can invoke this task as rake stories:all or rake stories:all story_prefix=user_ and the second variant allow you to run just one (or a subset) of the stories which turned out to be useful as they take a long time to run. Now we'll move onto stories/run_with_all_steps.rb which is what actually runs the stories.

First some setup of the environment since we're invoked from the command line


file_pattern = ARGV[0] || "*"
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'


Then two helper functions that load all stories in a directory or use the story_pattern passed in. This is something I haven't seen other people do with the RSpec story framework but seemed natural to me as I didn't want to have to maintain a list of stories to run and possibly (or probably) forget to add new ones. Stories also have a concept of steps that go along with stories but I didn't want to have to to manually associate steps with stories so decided to load them all. There were some clashes with different step files using the same phrase to define their steps but when that happened we just changed the phrasing of one or the other. The only trick in here is the sort on the step_files which was required because windows and the mac and linux loaded the files in a different order causing inconsistency across machines.


def all_local_stories(story_pattern = "")
story_path = "#{File.dirname(__FILE__)}/stories"
Dir[File.expand_path("#{story_path}/**/#{story_pattern}*.story")]
end

def all_steps_for_local_stories
steps_path = "#{File.dirname(__FILE__)}/steps"
step_files = Dir[File.expand_path("#{steps_path}/*.rb")].sort
step_files.collect do |file|
require file
file.match(/(\w+)\.rb\Z/).captures[0].underscore.to_sym
end
end


Lastly we invoke the rspec story runner. What's tricky here is that the story runner doesn't run here but registers itself using Kernel.at_exit (which I'd never seen before)


stories = all_local_stories(file_pattern)
stories.each do |file|
with_steps_for(*all_steps_for_local_stories) do
run file, :type => RailsStory
end
end


So up to here we can invoke our stories using rake ... but what are stories and how do they work? Stories are just text files written with the template


Story: Manage Users
As an Administrator
I want to manage the users who have access to my application
So that private stuff isn't seen by the wrong people

Scenario: Revoke access for a user
Given Alex is a registered user
When I delete the user Alex
Then Alex should not be able to access the sensitive content


This is pretty cool ... so how does it work? This is where the steps come in. We define a steps file to match each of the Given/When/Then clauses in our story and define what to do for each one.


require File.dirname(__FILE__) + '/../../../spec/spec_helper'

steps_for(:user_management) do
Given('$user_name is a registered user') do |user_name|
@users ||= []
@users << name =""> user_name})
end

When('I delete the user $user_name') do |user_name|
this_user = @users.detect {|a_user| a_user.name == user_name}
this_user.should_not be_nil
this_user.destroy
end

Then('$user_name should not be able to access the site') do |user_name|
get("/login?username=#{user_name}")
response.response_code.should == 401
end
end


As the rspec story runner parses the story file it looks for a step that matches invokes its block passing a parameter for each $-starting variable in the clause. The way I've written what's above we try to maintain some consistency between the variables referred to in the different clauses. If you refer to the user_name as 'Alex' in the Given clause we store that user away in the @users array and load it from there later on when we want to refer to the same user in the When clause.

We've had pretty good success working with stories in this way and as we move forward are going to continue to experiment with

  • Having our business analysts write most of the story files and the developers write the steps.
  • Thinking of ways to keep the steps files DRY
  • Working with WATIR or Selenium to create stories that drive the browser

Undefining a constant in a rspec test

over 3 years ago | Alex Rothenberg: Common Sense Software

I want to share something I just figured out. I had to write some code that used a class from a plugin if it was there and but did not require the plugin. The problem I faced was how to test this.

My method looked like this

class User 
def lookup_additional_person_info
if defined? CommonServices::Person
person = CommonServices::Person.find_by_id(@person_id)
@address = person.address
else
@address = 'not available'
end
end
end

I knew how to define CommonServices::Person to test the positive case but what about the negative? I found out you can undefine a class by sending its parent :remove_constant

  it 'should default additional parameters if CommonServices::Person is not defined' do
defined?(CommonServices::Person).should be_nil
user = User.new(:person_id=123)
user.lookup_additional_person_info
user.address.should == 'not available'
end

it 'should not populate additional parameters if CommonServices::Person is not defined' do
begin
defined?(CommonServices::Person).should be_nil
module CommonServices
class Person
end
end
CommonServices::Person.expects(:find).with(123).returns(person=mock)
person.expects(:address).returns(address_from_service=mock)

user = User.new(:person_id=123)
user.lookup_additional_person_info
user.address.should == address_from_service
ensure
CommonServices.send(:remove_const, :Person)
end
end

Testing with RSpec stories

over 3 years ago | Alex Rothenberg: Common Sense Software

I recently completed a project where we used RSpec stories for our integration testing and wanted to share some of my experiences. Overall RSpec Stories are an incredibly powerful tool and have allowed us to take a huge step towards business analysts writing functional specifications (in the form of plain text stories) then allowing the developers to make them pass. Today I'd like to share how we setup our stories and some of the decisions we made along the way.

The first goal was to run the stories from rake so we created a rake task in lib/tasks/stories.rake to run the stories and fail if any of them fail - this was useful when running this from our cruise server.


task :all do
args = ENV['story_prefix'] || ''
stories_passed = system 'ruby', File.join(RAILS_ROOT, "stories", "run_with_all_steps.rb"), args
raise('story failed') unless stories_passed
end


You can invoke this task as rake stories:all or rake stories:all story_prefix=user_ and the second variant allow you to run just one (or a subset) of the stories which turned out to be useful as they take a long time to run. Now we'll move onto stories/run_with_all_steps.rb which is what actually runs the stories.

First some setup of the environment since we're invoked from the command line


file_pattern = ARGV[0] || "*"
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'


Then two helper functions that load all stories in a directory or use the story_pattern passed in. This is something I haven't seen other people do with the RSpec story framework but seemed natural to me as I didn't want to have to maintain a list of stories to run and possibly (or probably) forget to add new ones. Stories also have a concept of steps that go along with stories but I didn't want to have to to manually associate steps with stories so decided to load them all. There were some clashes with different step files using the same phrase to define their steps but when that happened we just changed the phrasing of one or the other. The only trick in here is the sort on the step_files which was required because windows and the mac and linux loaded the files in a different order causing inconsistency across machines.


def all_local_stories(story_pattern = "")
story_path = "#{File.dirname(__FILE__)}/stories"
Dir[File.expand_path("#{story_path}/**/#{story_pattern}*.story")]
end

def all_steps_for_local_stories
steps_path = "#{File.dirname(__FILE__)}/steps"
step_files = Dir[File.expand_path("#{steps_path}/*.rb")].sort
step_files.collect do |file|
require file
file.match(/(\w+)\.rb\Z/).captures[0].underscore.to_sym
end
end


Lastly we invoke the rspec story runner. What's tricky here is that the story runner doesn't run here but registers itself using Kernel.at_exit (which I'd never seen before)


stories = all_local_stories(file_pattern)
stories.each do |file|
with_steps_for(*all_steps_for_local_stories) do
run file, :type => RailsStory
end
end


So up to here we can invoke our stories using rake ... but what are stories and how do they work? Stories are just text files written with the template


Story: Manage Users
As an Administrator
I want to manage the users who have access to my application
So that private stuff isn't seen by the wrong people

Scenario: Revoke access for a user
Given Alex is a registered user
When I delete the user Alex
Then Alex should not be able to access the sensitive content


This is pretty cool ... so how does it work? This is where the steps come in. We define a steps file to match each of the Given/When/Then clauses in our story and define what to do for each one.


require File.dirname(__FILE__) + '/../../../spec/spec_helper'

steps_for(:user_management) do
Given('$user_name is a registered user') do |user_name|
@users ||= []
@users << name =""> user_name})
end

When('I delete the user $user_name') do |user_name|
this_user = @users.detect {|a_user| a_user.name == user_name}
this_user.should_not be_nil
this_user.destroy
end

Then('$user_name should not be able to access the site') do |user_name|
get("/login?username=#{user_name}")
response.response_code.should == 401
end
end


As the rspec story runner parses the story file it looks for a step that matches invokes its block passing a parameter for each $-starting variable in the clause. The way I've written what's above we try to maintain some consistency between the variables referred to in the different clauses. If you refer to the user_name as 'Alex' in the Given clause we store that user away in the @users array and load it from there later on when we want to refer to the same user in the When clause.

We've had pretty good success working with stories in this way and as we move forward are going to continue to experiment with

  • Having our business analysts write most of the story files and the developers write the steps.
  • Thinking of ways to keep the steps files DRY
  • Working with WATIR or Selenium to create stories that drive the browser

Reserches

over 3 years ago | Surbhi Bhati: Clean Desk, Jammed drawers

Sometimes I think, you know with all these advent of Science and Technology, the way we have come so far, and the realization of how far we have yet to go, so scientists is all parts of the world are carrying out researches which are supposed to give us more insight on us, the way we are, the way we behave etc. But I don't understand sometimes what to believe of these researches, I mean one research says, Eggs are healthy for us, another says they are not. One says Milk is good, another says taking Milk is harmful.The point I think is, if you go on doing to much analysis, nothing can be said specifically, because at the core everything reacts differently to different things.
So getting what I want to say? NO!!
Don't bother, even I don't!!
P.S. - Most of the time ;-)

You are here

over 3 years ago | Surbhi Bhati: Clean Desk, Jammed drawers

So, I have just finished Meenakshi Reddy Madhavan's much talked about book 'You are here'. When I first read about it (in some newspaper), I had a great anticipation of reading a book, which talks about women of our generation, something I thought I'd be able to relate to. However as I actually started reading it, I realized I was not at all able to relate to any of its characters. Then I braced myself thinking, it might be because I come from a different social background and all, but even after that, I did not find anything at all in the book, which I can even call appealing, let alone worth reading. You know all the time I was reading it, searching for one single thing which will make me say, 'yeah, its nice', but NO!! I found I was expecting too much of Ms. Madhavan, I mean for all it might just have been a random collection of hundreds of her blogs she has written over the years. Its not even a good timepass. See -- There is no story, and every time something comes up, where you sense a beginning of something interesting, it has to precede first with some random and extra long flashback of some entirely uninteresting past, and more so totally unrelated to the thing going on in present, and it all burst as it had popped up..

I am not sure thought you know, I mean I might be a little harsh, due to that fact that I was expecting too much out of it, but still I'd say, it does not account for a good reading at all, and have read some good books (HummYeah..I think I have!!)

Being Women

over 3 years ago | Surbhi Bhati: Clean Desk, Jammed drawers

I wonder how boys can come up every time and comment on how a girl or women is looking fat, out of shape or whatever worse adjective they can come up with. And I can bet all girls, once in their life have asked a question that what is it that makes boys to comment on how a girl is looking, or rather 'should look'. I mean who are you to decide on what are the correct bodily measures for a girl? I mean they sometimes wont even show the decency of not commenting about it in public, and here, I am talking about they guys you know, like you buddy, brother or some colleague you are quite friendly with. They all have got this right about asking you a question if you get that little nanometer out of your normal frame.I can imagine what was it that made only girls to take all the pains in the world to look beautiful, and wats more, you are not considered as a appealing person if you do not do that. Never mind!! I think its just one of the endless unfair things we come across everyday, and care of give a thought about.