Archive for March, 2010

Comfort zones 0

One thing I learned early on is that in order to continue to learn and grow it is necessary to step outside your established “comfort zone.” That actually can have various meanings, depending on the person and the situation. For example, it may simply mean that you need to step outside of a frame of mind that is familiar to you and think about something in a slightly different way. Or, it could mean that in order to gain an understanding of a new concept, you realize that you are no longer the “master” or “senior developer” but instead a “newbie” or “junior” once again. In some situations your comfort zone may have more to do with where you live and who you’re surrounded by, and in order to really see things begin to move forward again, you feel that you need to change the status quo.

That concludes my too-long-for-Twitter musings for now :)

JQuery textbox watermark revisited: making it a plugin 1

In a previous post I talked about how you can do a textbox watermark very easily using JQuery. I’m long overdue in showing how you can take that same JQuery code and make it into a simple plugin.

So why would we make it a plugin? A JQuery plugin gives you the chance to encapsulate JQuery functionality into a single, pluggable module which can then be activated by calling a single method (with optional settings passed to it). If you’ve used things like the JQuery Validation library, you’ve seen a bit of the power of plugins. They are not hard to write, and the official documentation on the JQuery site gives you enough information to get started.

We will start off with the same basic HTML text box that we had in the previous post:

<input type="text" id="textBox1" />

And some simple CSS to go with it:

.watermark { color: #CCC; font-style: italic; }

So, just to recap what we want to do: when our focus is off of the textbox, and the textbox is empty, we want to display the default text, which is “type here…” in a light gray color. When the focus is on the textbox and the default text is displayed, we want to clear the box and the styling. If they type something in the box and then click elsewhere, we want to keep the new text and not reactivate the watermark style.

Using the previous JQuery code I posted as a base, here is the plugin code with comments to explain how it works:

(function($) {
	// Create a plugin with the name 'watermark'
	$.fn.watermark = function() {
		// Loop through the elements in the selector that we call the plug-in on
		this.each(function() {
			// Apply defaults to the box
			$(this).addClass("watermark").val("type here...");

			// Apply our focus and blur events
			// When we click on the field, we expect it to clear the field and remove the watermark
			$(this).focus(function() {
				$(this).filter(function() {
					// Check to see if we have a blank field or the default text
					return $(this).val() === "" || $(this).val() === "type here...";
				}).val("").removeClass("watermark");
			});

			// When we click off of the field, we expect it to replace the watermark,
			// unless we have entered text
			$(this).blur(function() {
				$(this).filter(function() {
					// Check to see if the field is blank
					return $(this).val() === "";
				}).addClass("watermark").val("type here...");
			});
		});
	};
})(jQuery);

To apply the plugin to our text box above we can now simply do this:

$(function() {
	$("#textBox1").watermark();
});

Making it better

What we’ve done so far is fine if you’re only concerned with a specific case, but what if we wanted to use this same plugin for different default text scenarios, or with different CSS classes? As it turns out, this is not hard to do.

We’re going to add two simple options to our plugin, but leave our defaults as they currently are:

(function($) {
	// Create a plugin with the name 'watermark'
	$.fn.watermark = function(settings) {
		// Defaults
		var config = {
			watermarkClass: 'watermark',
			defaultText: 'type here...'
		};

		// merge the passed in settings with our default config
		if(settings) $.extend(config, settings);

		// Loop through the elements in the selector that we call the plug-in on
		this.each(function() {
			// Apply defaults to the box
			$(this).addClass(config.watermarkClass).val(config.defaultText);

			// Apply our focus and blur events
			// When we click on the field, we expect it to clear the field and remove the watermark
			$(this).focus(function() {
				$(this).filter(function() {
					// Check to see if we have a blank field or the default text
					return $(this).val() === "" || $(this).val() === config.defaultText;
				}).val("").removeClass(config.watermarkClass);
			});

			// When we click off of the field, we expect it to replace the watermark,
			// unless we have entered text
			$(this).blur(function() {
				$(this).filter(function() {
					// Check to see if the field is blank
					return $(this).val() === "";
				}).addClass(config.watermarkClass).val(config.defaultText);
			});
		});
	};
})(jQuery);

Now, we can customize how we use the plugin:

$(function() {
	$("#textBox1").watermark({
		watermarkClass: 'grayText',
		defaultText: 'search'
	});
});

Files

Useful links

Getting Cucumber, RSpec, and Mongoid to play nice 3

Out of the box, Cucumber is setup to work with Active Record and uses the database_cleaner gem to ensure that the database is clean on each run. database_cleaner does support a few object mappers, but Mongoid is not one of them. It is fairly simple, however, to get a Cucumber and RSpec setup that works with Mongoid, and ensures your test database is clean on each run.

First, if you need help getting Mongoid setup, I recommend you take a look at the documentation on the Mongoid website. That will get you up and running. Before making any other changes, go ahead and run the rspec and cucumber generators for Rails to get the necessary files. Once that is done, make sure that if you’re not using Active Record (and since you’re using Mongoid and MongoDB I’m assuming you’re not unless you have a second database), you exclude it in your config/environment.rb:

config/environment.rb
RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
require File.join(File.dirname(__FILE__), 'boot')

Rails::Initializer.run do |config|
  config.gem "cucumber"
  config.gem "mongoid"

  config.frameworks -= [ :active_record ]
end

Next we will configure RSpec to clean our database before each spec is run. We can do this by configuring RSpec as follows:

spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment'))
require 'spec/autorun'
require 'spec/rails'
require 'mongoid'

Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}

Spec::Runner.configure do |config|
  config.before(:each) do
    Mongoid.master.collections.each(&:drop)
  end
end

And, finally, we need to configure Cucumber. I will admit, I had to play around with this for a while to get the correct setup. At one point, I had a situation where my database was only being cleaned correctly every other run. This actually turned out to not be a problem with my configuration, but with my scenarios (I am still somewhat of a Cucumber noob). In a fashion similar to that of our RSpec configuration above, we will simply tell Cucumber to wipe out the collections before each scenario runs:

features/support/env.rb
ENV["RAILS_ENV"] ||= "cucumber"
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')

require 'cucumber/formatter/unicode'
require 'cucumber/rails/world'
require 'cucumber/web/tableish'
require 'webrat'
require 'webrat/core/matchers'
require 'spec/expectations'
require 'mongoid'

Webrat.configure do |config|
  config.mode = :rails
  config.open_error_files = false
end

Cucumber::Rails::World.use_transactional_fixtures = false

Before do
  Mongoid.master.collections.each(&:drop)
end

I am using “before” hooks on both RSpec and Cucumber. My reasoning behind this is to ensure that when something is running, it starts off with a clean slate. I could easily do an “after” and just have each one clean up after itself, but this way, if I just want to run a single spec or scenario and inspect the data in my test database afterwards, I have that option.

So far learning Cucumber and RSpec has been a great experience and both have really made Behavior Driven Development (BDD) click.

Useful links