Logo

Modifying the auto_complete Plugin to Allow Repeated Fields

about 1 year ago | Pat Shaughnessy: Pat Shaughnessy - Home

Update March 2009: I reimplemented the code from this article in a better way and posted it in a fork of auto_complete on github; see: http://patshaughnessy.net/repeated_auto_complete.... however, the basic ideas below still apply.

Last week I ran into trouble trying to use the auto_complete plugin like this for a form containing a single group name field, but a series of repeated people name fields:
<p>
  Group <%= f.label :name %><br />
  <%= text_field_with_auto_complete :group, :name,
                                       {}, {:method => :get} %></p>
<% for person in @group.people %>
  <% fields_for "group[person_attributes][]", person do |person_form| %>
    <p>
      Person <%= person_form.label :name %><br />
     <%= text_field_with_auto_complete :person, :name, {:index => nil},
                                      {:method => :get}  %></p>
    <% unless person.new_record? %>
      <%= person_form.hidden_field :id, :index => nil %>
    <% end %>
  <% end %>
<% end %>

I want to just display a text field repeated once for each person record. The problem is that the text_field_with_auto_complete macro returns HTML and Javascript that doesn’t work when it’s repeated many times. How can I to get this to work?

Let’s start by writing the code we would like to use, or wished could work someday. The key detail in the form above is the name of the object in fields_for:
fields_for "group[person_attributes][]", person do |person_form|

This name is used on the server to mass-assign the attributes of all of the person records to the parent group record. The only way text_field_with_auto_complete could possibly work is if it generated an <input> tag with the desired name: “group[person_attributes][][name]”. I could just pass this value into text_field_with_auto_complete, but that not be very DRY since I would have to repeat the name more than once.

What if I could just call text_field_with_auto_complete directly on the person_form object yielded by fields_for? For example:
<% fields_for "group[person_attributes][]", person do |person_form| %>
  <%= person_form.text_field_with_auto_complete :person, :name %>

Then this call could generate an <input> tag with a name generated from the fields_for object name and my form code would remain very simple, readable and maintainable.

I found a way to do this following John Ford’s example (Writing a Custom FormBuilder in Rails) by adding a new version of form_for and fields_for to ActionView. First I modified the auto_complete plugin’s init.rb file and added a line to mixin a new form helper module into ActionView like this:
ActionView::Base.send :include, AutoCompleteFormHelper
Then our new AutoCompleteFormHelper class will look like this:
module AutoCompleteFormHelper
  [:form_for, :fields_for, :form_remote_for, :remote_form_for].each do |meth|
    src = <<-end_src
      def auto_complete_#{meth}(object_name, *args, &proc)
        options = args.last.is_a?(Hash) ? args.pop : {}
        options.update(:builder => AutoCompleteFormBuilder)
        #{meth}(object_name, *(args &lt;&lt; options), &proc)
      end
    end_src
    module_eval src, __FILE__, __LINE__
  end
end

What this does is create new ActionView methods called “auto_complete_form_for,” “auto_complete_fields_for,” etc. These methods simply call the original form_for and fields_for but pass in an additional option “:builder” that indicates ActionView should yield a different class to the form block… a new class I will write called “AutoCompleteFormBuilder,” instead of the original FormBuilder class. Now by writing AutoCompleteFormBuilder we have the ability to implement the behavior we need from text_field_with_auto_complete.

Here’s what I ended up with:

class AutoCompleteFormBuilder < ActionView::Helpers::FormBuilder
  def text_field_with_auto_complete(object, method, tag_options = {},
                                    completion_options = {})
    @template.text_field_with_auto_complete(
      "#{object}_#{Object.new.object_id}",
      method,
      { :name => "#{@object_name}[#{method}]" }.update(tag_options),
      { :param_name => "#{object}[#{method}]",
        :url => { :action => "auto_complete_for_#{object}_#{method}" }
      }.update(completion_options)
    )
  end  
end
This simply calls the original text_field_with_auto_complete with slightly different parameters:
  • We generate a unique number and add it as a suffix to the id attribute that will be generated for the <input> tag, and referenced in the Javascript:
    "#{object}_#{Object.new.object_id}"
    Now each <input> tag will have a unique id attribute, and the script.aculo.us autocomplete Javascript will work unmodified. Without this change, all of the repeated <input> tags would have the same id, and the autocomplete code would fail trying to find the ambiguous id on the page.
  • We pass in the object name from the fields_for or forms_for declaration as the name attribute for the <input> tag:
    { :name => "#{@object_name}[#{method}]" }.update(tag_options)
    Now our model code works as originally intended when the form is submitted, i.e. the "person_attributes" value will be mass-assigned to our child model objects.
  • We get the autocomplete behavior to work by telling it explicitly to use the simple “person[name]” object name in both the parameter name and URL for the AJAX request:
    { :param_name => "#{object}[#{method}]",
      :url => { :action => "auto_complete_for_#{object}_#{method}" }
    }.update(completion_options)
    This allow us to continue to use the same simple declaration in the controller that handles the auto complete requests:
    auto_complete_for :person, :name

Next step: Post this on GitHub.

Time boxing releases

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

This year has been great for us as an application development organization especially when it comes to project delivery. Most of our projects were delivered on time and on budget. You may be wondering how is that possible? Here is the trick..

Many factors contributed to the success. However, I attribute a great part of that success to two things: time boxed releases and intact teams. All our projects are now not more than three months long. When we start a new project, we set the expectation with the project team and the stakeholders that this project cannot go one day more than three months. This does a lot of good things. Since we know the burn rate of the team, the budget is fixed. The only thing that is negotiable is scope. Time boxing forces sponsors to prioritize the stories/requirements, and this makes sure that only high business value stuff gets done. So at the end of the project usually sponsors are happy because their high priority stuff got done. In the past these projects used to go on for ever until the sponsors exhausted their list, which includes nice-to-have stories. This also overcomes procrastination, sharpens the focus and increases motivation for the team.

Having said that, the key for teams is how they communicate this to the sponsors. If you tell them that we are going to pull the plug after three months, that usually creates panic and unnecessary push back from them. Instead, I would tell them that as an app dev organization we have decided to time box all our projects to three months. If you have highly critical requirements that wont fit into this release, we can have another release/project immediately following the first one. That kind of message usually does the trick and I have seen it work for many of our teams.

Some would say that this will work only for maintenance projects. In fact some of our teams have made this work for large greenfield projects. We broke those projects into three month chunks and made sure that the team delivers working software while adding some business value.

Have you tried anything similar?

Time boxing releases

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

This year has been great for us as an application development organization especially when it comes to project delivery. Most of our projects were delivered on time and on budget. You may be wondering how is that possible? Here is the trick..

Many factors contributed to the success. However, I attribute a great part of that success to two things: time boxed releases and intact teams. All our projects are now not more than three months long. When we start a new project, we set the expectation with the project team and the stakeholders that this project cannot go one day more than three months. This does a lot of good things. Since we know the burn rate of the team, the budget is fixed. The only thing that is negotiable is scope. Time boxing forces sponsors to prioritize the stories/requirements, and this makes sure that only high business value stuff gets done. So at the end of the project usually sponsors are happy because their high priority stuff got done. In the past these projects used to go on for ever until the sponsors exhausted their list, which includes nice-to-have stories. This also overcomes procrastination, sharpens the focus and increases motivation for the team.

Having said that, the key for teams is how they communicate this to the sponsors. If you tell them that we are going to pull the plug after three months, that usually creates panic and unnecessary push back from them. Instead, I would tell them that as an app dev organization we have decided to time box all our projects to three months. If you have highly critical requirements that wont fit into this release, we can have another release/project immediately following the first one. That kind of message usually does the trick and I have seen it work for many of our teams.

Some would say that this will work only for maintenance projects. In fact some of our teams have made this work for large greenfield projects. We broke those projects into three month chunks and made sure that the team delivers working software while adding some business value.

Have you tried anything similar?

Genius!

about 1 year ago | Megan Blocker: Queenie Takes Manhattan

When I have people over for dinner, there's inevitably a hunk of cake or pie left over. I try my darndest to foist it off on people, knowing that I will otherwise attack it maniacally later that night in between rounds of dishwashing. The problem, of course, is that this is Manhattan, and there's just no easy way to take a piece of cake home without balancing it precariously for the length of a subway or cab ride.

No longer! My friend Amanda just started a blog about crafting, design and baking (a bit of everything, really), and one of the projects she's shared are pie-slice shaped carryout boxes. The template is available online through Martha Stewart Living, and seems like a great way to distribute all those pesky leftover slices of pie and cake.I will hitting Kate's Paperie shortly to buy some supplies...thanks, Amanda!

Once you go local...

about 1 year ago | Megan Blocker: Queenie Takes Manhattan

Blue Hill at Stone Barns is, for many New York food-lovers, a place of near-mythic importance. One does not visit; one pays homage, or, at the very list, makes a pilgrimage. Its chef, Dan Barber, is justifiably famous for his pioneering role in the evolution of haute barnyard cuisine, and the food at the restaurant is truly delicious. I first visited last December, and, due to a Christmas season traffic jam, my group arrived too late to take our planned jaunt around the farm. So when Lisa and I were looking for something to do last Saturday afternoon, we decided to get in her car and head across the Tappan Zee to the Stone Barns Center in Tarrytown.

Stone Barns sits on the old Rockefeller estate in the Pocantico Hills section of town. The buildings themselves date from the 1930's, and were first part of a dairy farm, and later the location of Peggy Rockefeller's cattle ranch. In 2004, the Rockefellers invited Dan Barber to open a for-profit restaurant as part of the larger not-for-profit agricultural and educational experiment - a center for learning about environmentally responsible, four-season farming with a restaurant to match.

It's easy to see why the experiment took root so quickly and so deeply. The property itself is stunning - all gentle slopes and rolling hills, with the stately barns rising out of a hollow in the valley. Walking around the pastures and the vegetable fields, you run into all sorts - parents pushing strollers, locals out for a morning jog, and foodies paying homage. In addition to the restaurant, there's a casual cafe open daily that serves strong, hot coffee (a must for keeping warm on long, blustery walks around the farm), sandwiches, salads and pastries. Almost all of the food on offer is grown or raised on the property, which means the menus are exclusively seasonal and change daily.

Lisa and I arrived around 1 o'clock, and started our visit with a quick trip to the gift shop. I know, I know - but, this is a truly excellent shop. I picked up a couple of $6 grocery tote bags - perfect stocking stuffers - and a fantastic (and challenging) trivia game called Foodie Fight. I also spied a truly gorgeous cookbook called Country Cooking of France, which is going on my Christmas list.

Since it was lunchtime, and since we needed fortification for our planned walk, we decided to have lunch next. The line snakes around the tiny cafe in a circle, which gave us ample opportunity to scope out all that was on offer. I grabbed a jar of cucumber pickles from the shelf, and thought seriously about the cow-printed tea towels and teeny coffee cakes. Also available: the decidedly not local but truly delicious Rancho Gordo heirloom beans.

We finally made it up to the counter, where we ordered the bologna sandwich with pickles, zucchini fritatta, caramel apple, apple custard tart, and two big, steaming cups of coffee. We camped out at a picnic table outside and wolfed down our feast.

The homemade bologna was the best bologna I have ever tasted - light in texture, tangy and meaty in flavor, and layered on a fluffy piece of focaccia spread with strong mustard. The apple tart was a bit too sweet, but the cookie crumble crust on the bottom was salty enough to cut the sugar. The caramel apple, served on a twig, was deliciously smoky, if tooth-crackingly tough to bite into. All in all, a good lunch.

Finally, filled with lunchtime goodness, we set off on a walk around the property. We walked past the private dining room, where the staff were setting up for a wedding, arranging flowers and building a chuppah out of twigs and berries from the farm and surrounding park. We saw a fallow vegetable field, walked past the dairy cows relaxing in their pasture, and paid a visit to the chickens, who gathered around our feet, pecking gently at our toes in search of a snack.

Just past the chickens were a flock of pure-white ducks and a few gigantic pigs. These guys had to be at least three hundred pounds each. As we approached, I remembered the bologna sandwich and started feeling a bit, well, guilty. But then I realized how happy the pigs were, rooting around in their trough, enjoying the sunshine, wandering at will, and realized it was not only the best-tasting bologna I'd ever eaten - it was also probably the most ethically sound.

All in all, that was the prevailing lesson of the day. Seeing where your food comes from, spending time with it, smelling the earth out of which it grew, picking your way across the field where it was raised - it makes you that much more appreciative of it, and far more aware of the impact your choices have. The peace of mind you gain by understanding how your food was produced cannot be overstated. Yes, it can be expensive and a bit of a hassle to eat locally and humanely, but if you can afford the little bit of extra money and a little bit more extra time, it truly is worth it.

Been there done that....time to move on

about 1 year ago | Mark Mintz: Mark Mintz

I totally agree with Jay Fields in that I too prefer jobs that allow me to learn new things. Exhibit A - My Career:

  1. Started working on as a analyst on a mainframe GL package
  2. Moved to PeopleSoft HR implementations (PeopleSoft HR was THE HR application at that time)
  3. From there moved on to BI/Reporting with Essbase (before Hyperion and Oracle)
  4. My next stop was an internet company building a custom Java based billing application (not sure why we didn't leverage the companies existing Oracle Financials for this but that is for a different post)
  5. When it came time to move on I went back to HR systems and then from there back to Finance applications....each time I focused on a different technology and functional area within the broader spectrum of Finance and HR
Some people might view this approach to my career as being a "jack of all trades, master of none" since I did not stick around long enough in any one of the areas to become a true expert in any one of the technologies or functional areas. I beg to differ. I believe that I gained two valuable things from this career track:

  1. A broad range of experiences and skills that I can draw on when moving into new areas
  2. The ability to learn new things quickly, figure out how much I need to learn in that area to be effective and then move on to the next thing I needed to learn
I also totally agree with Jay when he says:

Think of it as job security -- I shouldn't ever be out-of-date when it comes to technology experience. Think of it as an investment -- everything I learn creates a broader range of experience that I can leverage for future projects or jobs. Think of it as experimenting -- by trying many different solutions I may find ways to combine them and innovate.
I also think this type of career keeps it interesting. I can't imagine how unhappy I would be if I was still focusing on the same stuff I did at the start of my career. I've done that stuff, learned a lot and moved on. I've got that tool in my tool belt now and it's time to get some more tools. If I ever need to come back to that tool, sure it may need some honing but a hammer is still a hammer and I should be able to get the job done, even if the hammer is an old model.

Geeking out.

about 1 year ago | Megan Blocker: Queenie Takes Manhattan

So, how cool is this? One of my colleagues pointed the whole group to this nifty little tool today, and I just had to share. Type in your blog URL, or paste in a bunch of text, and you get this cool word cloud.

So addictive, so fun, and so revealing: I am clearly in love with sprouts.

Autocomplete plugin doesn’t work for repeated fields

about 1 year ago | Pat Shaughnessy: Pat Shaughnessy - Home

Recently I tried using the autocomplete plugin from Rails for the first time. It was a great way to quickly implement type-ahead Ajax behavior by writing little or no code. By simply adding two lines – one to my controller:

auto_complete_for :group, :name
and another to my form:
<%= text_field_with_auto_complete :group, :name, {}, {:method => :get} %>
users of my form were able to easily pick from a long list of group names simply by typing the first few letters. What I love about Rails is not only was I able to implement this complex feature with only 2 lines, but those 2 lines were very easy to read: my controller has to handle “autocompletion for group names” and my form should contain a “text field with auto complete behavior for group names”. It’s very natural and intuitive.

The only ugly detail here is that the “{:method => :get}” hash is required to avoid errors related to form security… without it I get an error like this:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.1.1/lib/action_controller/request_forgery_protection.rb:86:in 'verify_authenticity_token'
This discussion explains that we can avoid the security exception by forcing the autocomplete request to use GET instead of POST. Note that you also need to change config/routes.rb to map the GET request for the completion values to the autocomplete action in your controller. This is a bit ugly, but not a big deal.

However, once I tried adding additional fields to the form I ran into some real trouble. I enhanced my form by adding multiple child model objects; in this case I associated a series of people for each group. That is:

class Person < ActiveRecord::Base`
 belongs_to :group
and:
class Group < ActiveRecord::Base
 has_many :people
Following Ryan Bates’ great explanation on railcasts about how to implement a form for both parent and child records at the same time, I ended up with a form that looked like this:
<p>
  Group <%= f.label :name %><br />
  <%= text_field_with_auto_complete :group, :name, {}, {:method => :get} %>
</p>
<% for person in @group.people %>
  <% fields_for "group[person_attributes][]", person do |person_form| %>
    <p>
      Person <%= person_form.label :name %><br />
     <%= text_field_with_auto_complete :person, :name, {:index => nil}, {:method => :get}  %></p>
    <% unless person.new_record? %>
      <%= person_form.hidden_field :id, :index => nil %>
    <% end %>
  <% end %>
<% end %>
This looks a lot more complicated, but except for the “index => nil” hack (see part 3 of Ryan’s screen cast for an explanation) the code is very clean and straightforward to understand. We display the group name autocomplete text field as before, and then iterate over the people contained in this group, displaying a new autocomplete text field for each one.

Too bad it doesn’t work! Even worse, this code actually has 3 separate problems: First, the <input> tag generated by text_field_with_auto_complete is not what we want for the child objects. Without autocomplete, we would use:

<%= person_form.text_field :name %>
referring to the “fields_for” method just above and get this HTML:
<input id="group_person_attributes__name" name="group[person_attributes][][name]" size="30" type="text" />
But text_field_with_auto_complete :person, :name, … yields this instead:
<input id="person__name" name="person[][name]" size="30" type="text" />
along with CSS and Javascript for the auto complete behavior.

Second, the server side code added to my controller also doesn’t know where to look for the person name value in the actual parameter hash; instead it assumes it will receive the parameter as specified in the <input> above: params[“person”][“name”]. See vendor/plugins/autocomplete/lib/auto_complete.rb for details.

And the last and worst problem of all is that the DHTML/Javascript code inside of controls.js from script.aculo.us assumes that the <input> tag id passed in will be unique on the page. But since we will have many person fields for each group field, the script.aculo.us code will fail trying to location the <input> and <div> tags specified by text_field_with_auto_complete.

So what to do? The first time I solved this problem, I simply copied and hard coded the HTML and Javascript produced by the autocomplete plugin into my form erb file. Then I changed it to make it work. I also manually added a function to my controller on the server side to get it to find and process the parameter hash properly. If I were still writing PHP code, then I would probably assume this was sufficient and call it a day. If I happened to be using J2EE for this project, I’d probably look at creating a subclass of the autocomplete plugin somehow and extending/modifying the behavior as necessary. Then I would have yet another class and another JAR file to maintain and worry about – not quite as messy, but still ugly or at best complicated.

There must be a better way! Since I’m using Ruby I don’t need to settle for overly complicated code or ugly code. Instead I’ll try to develop a solution using what seems to me the Ruby philosophy: write the cleanest, tightest code you can that makes sense and then find a way to make it work. In my next post I’ll tell you what happened.

Team based performance evaluations

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

It is that time of the year again... Evaluation season. I am yet to start writing evaluations for my direct reports. Right now I am in the process of writing references for the folks who have asked me for it. While doing so the same nagging thought that haunted me last year reappeared ... is individual performance review the right way to do this. Probably not!

In our organization all the work is done by teams. Then why don't we reward teams instead of individuals? It seems like we are rewarding someone for their individual performance than for their team work, when all what we need is high performing teams.

Some may argue that there are some issues with team based performance evaluation. Here is one question I got from some of the folks I pitched this idea.... How will we form the teams?

There are many ways to do it. For example.. In the beginning of the year .. create a pool of people that includes FTEs (Full Time Employee) and vendor contractors with different skills (Scrum Masters, Product Owners, Architects, Developers etc.). We can create some basic rules like not more than two FTEs can be part of a team, and then ask everyone to self form teams. Teams can think about technology areas and functional areas as criteria to define team attributes. Keep the teams intact for rest of the year and at the end of the year, evaluate the teams and have the team members evaluate each other. The key for these teams is to self-organize and self- manage themselves. If someone is not doing well in the team and is getting a free ride the team should be able to vote him/her out.

I know it sounds a little bit radical. I am confident about the success though. This year I have been pushing hard on having "intact teams" and as an app dev organization we saw the value of having intact teams. Our internal clients love them. The start-up time for a new project has considerably reduced due to these teams as they have already passed the forming, storming and norming stages of team formation. These teams seem to knocking project after project out of the park. Our vendors and vendor employees also love it as we are giving them a long term commitment.

What are your thoughts on this topic?

By the way there was an interesting article on WSJ today..
Get Rid of the Performance Review!It destroys morale, kills teamwork and hurts the bottom line.

Team based performance evaluations

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

It is that time of the year again... Evaluation season. I am yet to start writing evaluations for my direct reports. Right now I am in the process of writing references for the folks who have asked me for it. While doing so the same nagging thought that haunted me last year reappeared ... is individual performance review the right way to do this. Probably not!

In our organization all the work is done by teams. Then why don't we reward teams instead of individuals? It seems like we are rewarding someone for their individual performance than for their team work, when all what we need is high performing teams.

Some may argue that there are some issues with team based performance evaluation. Here is one question I got from some of the folks I pitched this idea.... How will we form the teams?

There are many ways to do it. For example.. In the beginning of the year .. create a pool of people that includes FTEs (Full Time Employee) and vendor contractors with different skills (Scrum Masters, Product Owners, Architects, Developers etc.). We can create some basic rules like not more than two FTEs can be part of a team, and then ask everyone to self form teams. Teams can think about technology areas and functional areas as criteria to define team attributes. Keep the teams intact for rest of the year and at the end of the year, evaluate the teams and have the team members evaluate each other. The key for these teams is to self-organize and self- manage themselves. If someone is not doing well in the team and is getting a free ride the team should be able to vote him/her out.

I know it sounds a little bit radical. I am confident about the success though. This year I have been pushing hard on having "intact teams" and as an app dev organization we saw the value of having intact teams. Our internal clients love them. The start-up time for a new project has considerably reduced due to these teams as they have already passed the forming, storming and norming stages of team formation. These teams seem to knocking project after project out of the park. Our vendors and vendor employees also love it as we are giving them a long term commitment.

What are your thoughts on this topic?

By the way there was an interesting article on WSJ today..
Get Rid of the Performance Review!It destroys morale, kills teamwork and hurts the bottom line.

Country girls.

about 1 year ago | Megan Blocker: Queenie Takes Manhattan

I spent the weekend with my too-cool friend Lisa, and yesterday afternoon we drove across the Tappan Zee from her perch in New Jersey to pay a visit to Blue Hill at Stone Barns, home of one of the best restaurants in the country. But we weren't there to eat in the fancy-schmancy dining room - we were there to explore the farm, which is open to any and everyone, complete with a delicious café and free-range chickens.

I promise an in-depth post later, but I couldn't resist sharing the curiosity of this adorable, adventurous little one, who made friends with the flock of hens he met in the pasture. The lesson? Get to know your food, and do it young.

Funny thing.

about 1 year ago | Megan Blocker: Queenie Takes Manhattan

I was reading back over my posts recently, and I realized that I have never, ever written a post about my absolute favorite food of all time: cucumbers.

Sometimes people are surprised when I tell them that cucumbers are my favorite food - I think they expect me to cite something more exotic, like duck confit, or frisee aux lardons, or maybe pho. But, no - when nothing else appeals, when nothing else will do, cucumbers are where it's at for me.

And lest you think that I'm some sort of johnny-come-lately where cucumbers are concerned, let me tell you a little story. When I was in elementary school, my parents had to visit the school each year for the obligatory Parents' Night. One year (maybe my mom will chime in on the comments and tell us which), the teacher had each of the parents guess which desk belonged to their child my reading the children's answers to a set of questions. One of the questions was, "What's your favorite food?" The other kids mainly answered "pizza," or "spaghetti" or "steak" - but my answer was "cucumbers."

To this day, I go through about two pounds of cucumbers each week - I slice them into sticks and snack on them on weekend afternoons, I chop them into salads almost every night with dinner, and sometimes I even cook with them. They're great in stir-fries, where they add a bit of freshness and crunch; seeded and finely chopped, they add texture and contrast to chicken and tuna salad; and, thinly sliced and lightly sauteed in a bit of butter, salt and pepper, they become a luscious side dish all their own.

I love cucumbers in all their incarnations - Kirby, Persian, English, standard - no matter which, the savory melon-like flavor keeps me coming back again and again. I suspect I'll never tire of cucumbers - I'll probably still be picking them out of salad bowls right up until the end of time.

If you smell something stinking, flog it!!!

about 1 year 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

Role of External Coaches

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

We are in the process of rolling out Scrum firm wide. We are almost two years into the journey. We would not have achieved our current maturity in Scrum without the help of External coaches. In the beginning itself we decided to partner with some of the big names in the Scrum world. Selling to others in the organization becomes much easy when it comes from external coaches. What is your experience in this?

Role of External Coaches

about 1 year ago | Biju Bhaskar: Thoughts on enterprise application development...

We are in the process of rolling out Scrum firm wide. We are almost two years into the journey. We would not have achieved our current maturity in Scrum without the help of External coaches. In the beginning itself we decided to partner with some of the big names in the Scrum world. Selling to others in the organization becomes much easy when it comes from external coaches. What is your experience in this?

Agile and Ruby On Rails - hand in hand?

about 1 year 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)

Experiencing technical proficiency gap in your offshore team?

about 1 year ago | Prasoon Sharma: Enterprise Software Does not Have to Suck

There is a distinct gap in the technical proficiency we need to build a quality software product vs. what is offered by our offshore software development partner today.

Over the years, we've asked them to bridge this gap (raise technical competency level) by getting experienced/talented engineers in the pool... but it hasn't happened yet...

So we're addressing it as follows. What are you doing?

  • Bridge the gap from our end - i.e. build and maintain a pool of talented engineers (FTE, Consultants) onshore to make key technical decisions. For this we've hired top talent in Java/Ruby/SQL and trained our engineers to be hands-on.
  • Encourage offshore partner to hire/build/retain talented technologists - with techniques like offering higher rate for such engineers offshore

Using Ruby for shell scripting

about 1 year 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

about 1 year 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!

Groups and Networks

about 1 year ago | Michael Idinopulos: Transparent Office

Stowe Boyd recently posted the following statement: I disagree with the notion that Enterprise 2.0 is about groups not the individual. On the contrary: Web 2.0 is based on the person and personal relationships in networks, not group membership. It...

Undefining a constant in a rspec test

about 1 year 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

Undefining a constant in a rspec test

about 1 year 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

about 1 year 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

Testing with RSpec stories

about 1 year 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

Undefined method "updated?"

about 1 year ago | Amit Kumar: RubyizednRailified

I was juggling around to find out the cause. I kept on getting error:

NoMethodError (undefined method `updated?' for #): /vendor/rails/activerecord/lib/active_record/attribute_methods.rb:251:in `method_missing' /vendor/rails/activerecord/lib/active_record/associations.rb:907:in `belongs_to_before_save_for_person' /vendor/rails/activesupport/lib/active_support/callbacks.rb:173:in `send' /vendor/rails/activesupport/lib/active_support/callbacks.rb:173:in `evaluate_method' /vendor/rails/activesupport/lib/active_support/callbacks.rb:161:in `call' /vendor/rails/activesupport/lib/active_support/callbacks.rb:93:in `run' /vendor/rails/activesupport/lib/active_support/callbacks.rb:92:in `each' /vendor/rails/activesupport/lib/active_support/callbacks.rb:92:in `send' /vendor/rails/activesupport/lib/active_support/callbacks.rb:92:in `run' /vendor/rails/activesupport/lib/active_support/callbacks.rb:272:in `run_callbacks' /vendor/rails/activerecord/lib/active_record/callbacks.rb:298:in `callback' /vendor/rails/activerecord/lib/active_record/callbacks.rb:206:in `create_or_update' /vendor/rails/activerecord/lib/active_record/base.rb:2200:in `save_without_validation' /vendor/rails/activerecord/lib/active_record/validations.rb:901:in `save_without_dirty' /vendor/rails/activerecord/lib/active_record/dirty.rb:75:in `save_without_transactions'

I have 2 models Person and Rate with

class Person
  has_many :rates
end

class Rate
  belongs_to :person
end

On create/update of person or rate, I kept on getting the error. After deep digging I found the cause of the issue.

In my Rate model, I was creating a @person object. The association (belongs_to, has_many etc..) calls before_save callback for associated model. Since I created a new @person object in my Rate class, it was calling 'updated?' for new object and hence NoMethodError.

I renamed the @person object and resolved the issue.

Hope this helps.

Be Happy

about 1 year 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

Reserches

about 1 year 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 ;-)

Android v/s iPhone

about 1 year ago | Amar Phadke: Amar Phadke's weblog

There is a lot of debate going on recently about which is a better mobile computing platform. Is it the iPhone or the Android? In my opinion, Android will emerge as a eventual winner. Here’s why: It’s developed on an open source platform (iPhone runs on a closed Cocoa platform thats runs on a scaled down [...]

You are here

about 1 year 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

about 1 year 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.