RubyGems mirror update
My RubyGems mirror just got an update :
- switch to rubygems
1.9.50.9.5 - index generation
If all goes well, it should be compatible with older rubygems versions and give a significantly boost to those using rubygems >= 1.9.5. 0.9.5[…]
Javascript BDD (2)
Just a quick add-on on my previous post, I found there was already a nice BDD framework for javascript : JSSpec.
Go see the demo page, it looks really nice.[…]
Git 1.5.3 : submodule
Here’s another new functionality introduce in Git 1.5.3 : submodule.
Submodules allows you to manage subparts of a project in different git repositories and reference them in a “superproject” repository.
You can see it as an equivalent to Subversion “externals”.
A simple usage exemple (in your “superproject” root) :
git submodule add http://mygitdomain.tld/my_sub_project
git submodule init
git submodule update- This will add the remote my_sub_project as an entry in .gitmodules and a directory named my_sub_project in your “superproject”.
- Then initialize all modules added, this means add them to .git/config
- And finally clone and checkout the different modules in their respective directories (created by the
git submodule addcommand)
[Update – 2007/12/05]
I forgot to add you should not work directly with the “superproject” git repository besides adding more submodules.
You should clone the “superproject” git repository first and then the submodule part will be usable.
git clone superproject superprojectcloned
cd superprojectcloned
git submodule init
git submodule updateand work from “superprojectcloned” repository.
Then when you want to include the last versions of the submodules, issue :
git submodule status
git submodule update- status, gives you an overview of the submodules state.
- update, fetch the latest version and reference the sha1 associated.[…]
Date, Time and old days
Not so long ago a bug emerged on my current application, a vicious one.
Let’s say you have a user and store its bithday date in database (say MySQL) as a DateTime (yep I know, it’s a little to precise for this kind of data).
Rails conveniently retrieve this field as a Time, but as you probably do not be aware of is the range limitation of Time’s dates. In fact the Time class is often used because it’s faster than Date or DateTime, but the tradeoff is it handle date based on epoch time (01-01-1970), where Date/DateTime use Complex class and as such can handle a broader range of date.
Well, my user’s birthday date was somewhere in 60s and so can’t be handle by a the Time class. But it’s not as simple, Rails fallback to DateTime if parsing MySQL datetime field does not work with Time.parse. This results in an object perfectly valid when using it, the problem arise when you try to update the corresponding record in database.
Remember my initial datetime field was fetched as a DateTime ruby object and as Rails include all fields from the record on update it tries to translate the birthday to something MySQL recognize. ActiveSupport include some convenients methods to do that : Time#to_s and Date#to_s takes a parameter allowing us to do something like Time.today.to_s(:db) as well as some convenient Date to Time conversion. Unfortunately, no out of range detection is done in Date#to_time which leads me to the first headache. Then ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS hash does not contains a :db key so calling Date#to_s(:db) results in an erroneous string for the db. (this is corrected in rails edge but not in 1.2-stable branch).
All in all :
- be aware of Time class limitation
- ruby open classes are wonderful : just drop this code in your lib directory and that should make it until rails is patched.
module ActiveSupport
module CoreExtensions
module Date
module Conversions
# Add db and rfc822 format
DATE_FORMATS.merge!({
:db => "%Y-%m-%d %H:%M:%S",
:rfc822 => "%a, %d %b %Y %H:%M:%S %z"
})
end
end
end
end RSpec On Rails and HAML
As my incursion into RSpec continue, I encountered a strange problem on my CC machine. I switched my default layout (aka application.rhtml) to HAML, so the file became application.haml. No problem on my development machine running specs either with spec command or with rake. But the CC machine start sending me error in my specs, telling me application.rhtml does not exists.
I searched some looooong minutes which stack generates this error, four possibilities : RoR, RSpec, Gettext, HAML plugin.
The last thing I did before the specs breaks was to switch a controller spec from view isolation to view integration. So I looked more closely to RSpec On Rails plugin and found something interesting, to run in isolation mode, RSpec on rails stub those methods :
@template.stub!(:file_exists?).and_return(true)
@template.should_receive(:render_template).any_number_of_times.and_return(true) { |*args|
@first_render ||= args[2]
}
@template.stub!(:find_template_extension_for).and_return("rhtml")
@template.stub!(:read_template_file).and_return("this is fake content generated by rspec")So it faint Rails about an existing application.rhtml which in fact does not. Rails in its great willingness cache the different template/template extension lookups even in test mode. Then when mixing view integration and view isolation in specs, if those with view integration run after those in isolation, it breaks… or it should, as I can’t reproduce this in my development machine nor can I reproduce this on my CC machine when launching the specs by hand with the spec command (it does occurs only with rake).
About the Rails choice to cache, I fully understand why it is as it is, there should be no template rename during test as it is possible in development mode. So it’s not Rails fault.
But what can we do about this, the simplest solution I came to was to set the template extension caching to off in config/environments/test.rb :
config.action_view.cache_template_extensions = falseHope this helps someone else using RSpecOnRails with a custom template system.
[Update]
The difference between my two machine was revealed to me in just after I post this. The rake task on the CC machine, does not sort the controller spec files as my development machine, the problematic file is called addresses_controller_spec.rb so it was the first one evaluate in my development machine and is drowned between others in my CC machine.[…]
How to use a custom RDoc template from a rake task
I just need to do that, use a custom rdoc template to generate a documentation for a Rails project.
My main concern, regarding the different solutions I found on the web, was I didn’t want to have a template in the ruby installation directory but one that resides in a subdirectory of my rails application.
In fact it’s not a problem … when you find the good command. Here’s my rake task to generate the documentation (I put the Jamis Buck customized template in RAILS_ROOT/doc/rdoc_template/jamis.rb)
namespace :doc do
desc "Generate documentation for the application"
Rake::RDocTask.new("app_jamis") { |rdoc|
rdoc.rdoc_dir = 'doc/app_jamis'
rdoc.title = "My Website Documentation"
rdoc.template = "doc/rdoc_template/jamis.rb"
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('doc/README_FOR_APP')
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
endThe important part is :
rdoc.template = "doc/rdoc_template/jamis.rb"I tried to use the —template= and -T options in rdoc.options but it didn’t work… then I found that the RDocTask appends its template variable to the options passed to RDoc bypassing mine.[…]
RSpec on Rails : RESTful Authentication
For a new Rail project I’m working on, I decided to try to use RSpec.
The main problem I see with this solution is that not so many plugins out there have RSpec tests and so it can be boring to have to run autotest and rspec_autotest in parralel to be sure both RSpec and Test::Unit tests passes.
Helped by a recent post of Yurii Rashkovskii : RSpec on Rails: acts_as_authenticated , I adapted and add a few specifications to be compatible with restful_authentication plugin.
Here are the files :
- user_spec.rb : User model specs
- users_controller_spec.rb : Users controller specs
- sessions_controller_spec.rb : Sessions controller specs
Just put those in spec/{models,controllers}, copy users.yml fixtures from test/fixtures to spec/fixtures/ and you’re done.
Note I did not spec the UserObserver for now.
[Update]
I refactored a little bit my previous files, add some specs and try to spec user_notifier and user_observer models as well.
- user_spec.rb : User model specs
- user_notifier_spec.rb : UserNotifier model specs
- user_observer_spec.rb : UserObserver model specs
- users_controller_spec.rb : Users controller specs
- sessions_controller_spec.rb : Sessions controller specs
You should also add :
def set_mailer_in_test
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
endto Spec::Rails::EvalContext class in your spec_helper.rb.
I’m waiting for your comments on notifier/observer specs, I didn’t see any examples of them in rspec and I’m not sure of the best way to spec them. For example, UserNotifier is an ActionMailer and resides in models directory, but it acts more like a controller (render views), well you see the point.
[Update]
For those of you who switch to RSpec 1.0.x, Jonathan Linowes has updated my files to make them compatible with the new syntax (thanks). You can grab them at : Rspec 1.0 and Restful Authentication[…]
Subversion Automation : automate branching/tagging
I recently discovered a gem (no joke here) called Subversion Automation, it’s aimed to provide an unified layout to handle your projects branching/tagging in subversion and more importantly automate the process through a simple command : sc .
It currently handle some key aspects of a project management :
- release :
- bug
- experimental
Subversion Automation is really worth a try, even if it could be hard to use on existing project due to the subversion layout changes implied, but it’s up to you.
Be sure to watch the marvelous screencast on author website : Subversion Automation Screencast[…]
Prototype's Enumerable
My previous posts about Prototype were focused on shortcuts and functions to work on forms. This one is all about the Enumerable object.
Well, Enumerable is one of those terrific objects of Prototype. Basically it allows you to add enumeration to your objects or add prototype’s way to do it to standard javascript objects. But as javascript doesn’t allow you to redefine the way your object (or standard object) implements enumeration, it broke the standard javascript enumeration. As you should know, javascript allows you to enumerate through an object properties ( with the “for in” syntax) and takes care to step over unwanted properties, for example :
var myArray = [1,2,3,4]; // Let's build an array object
for ( i in myArray ) {
console.info(i);
}
// Output to firebug console:
// 1
// 2
// 3
// 4As you’ve seen, even if Array is an object, the enumeration step over methods to only gives you values. If you do the same thing with Prototype library loaded, you’ll get the following :
var myArray = [1,2,3,4]; // Let's build an array object
for ( i in myArray ) {
console.info(i);
}
// Output to firebug console:
// 0
// 1
// 2
// 3
// each
// all
// any
// collect
// detect
// findAll
// ...So I let you imagine how this can break existing code. That’s why so many other javascript framework developers don’t like Prototype’s way of doing and in some way they are right, this is a constraint of Prototype you should be aware of : Prototype don’t play nicely with others.
Then, after this little precautions let’s examine what Prototype’s Enumerable objects can give us.
All you need, to add enumerability to your objects, is to define a function _each which takes a function as parameter and call this function with each iterable elements of your objects.
For example if your objects stores its elements in an Array, you can make your objects enumerable like this :
var MyObject = Class.create();
MyObject.prototype = {
initialize: function() {
this.elements = $A(arguments);
}
// The magic function :)
_each: function(iterator) {
for ( var i = 0; i < this.elements.length; i++)
iterator(this.elements[i]);
}
};
// Extends MyObject with Enumerable functions
Object.extend(MyObject.prototype, Enumerable);Now our new object have the following methods :
- each()
- all()
- any()
- collect()
- detect()
- findAll()
- grep()
- include()
- inject()
- invoke()
- max()
- min()
- partition()
- pluck()
- reject()
- sortBy()
- toArray()
- zip()
- inspect()
Nice for a single method added, isn’t it ?
Let’s see what we can do with this :
// Let's create a new MyObject object :)
var anObject = new MyObject(3,5,4);
// Iterate through values
anObject.each( function(element) {
doSomething(element);
});
// Verify if all our values are lesser than 6
// return true
anObject.all( function(value) {
return value < 6;
});
// Verify if any value is greater than 4
// return true
anObject.any( function(value) {
return value > 4;
});
// Get an array containing all values as string prefixed by 'id_'
// return ['id_3','id_5','id_4']
valuesAsId = anObject.collect( function(value) {
return 'id_' + value;
});
// Get the first element greater than 3
// return 5
anObject.detect( function(value) {
return value > 3;
});
// Find all elements greater than 3
// return [5,4]
anObject.findAll( function(value) {
return value > 3;
});
// Find all elements matching a given pattern
// here we assume our initialization was anObject = new MyObject('jonathan', 'tron');
// return ['jonathan']
anObject.grep(/h/)
// return ['JONATHAN']
anObject.grep(/h/, function(value){
return value.toUpperCase();
});
// Find if 3 is an element of our object
// return true
anObject.include(3);
// Get the sum of our elements + 10
// return 22
anObject.inject(10, function(sum, value) {
return sum + value;
});
// Want to call a method on each elements ?
// return ["3","5","4"]
anObject.invoke('toString');
// Want to call a method on each elements but this time with params ?
// return ["11","101","100"]
anObject.invoke('toString', 2);
// Get the greatest element
// return 5
anObject.max();
// Get the smallest element
// return 3
anObject.min();
// Separate the elements in two groups :
// those matching a condition and those no matching it
// return [ [3], [5,4] ]
anObject.partition( function(value) {
return value <= 3;
});
// Get an array of a particular properties of each element
// here we assume our initialization was :
// anObject = new MyObject( {firstname: 'jonathan', lastname: 'tron'},
// {firstname: 'sam', lastname: 'stephenson'});
// return ['jonathan','sam']
anObject.pluck('firstname');
// Return all element for whose the given function return true
// return [3]
anObject.reject(function(value) {
return value != 3;
});
// Sort using the given compare function
// here we assume our initialization was :
// anObject = new MyObject( {firstname: 'jonathan', lastname: 'tron'},
// {firstname: 'sam', lastname: 'stephenson'});
// return [{firstname: 'sam', lastname: 'stephenson'},{firstname: 'jonathan', lastname: 'tron'}]
anObject.sortBy(function(value) {
return value.lastname.toLowerCase();
});
// Get a new array of elements
// return [3,5,4]
anObject.toArray();
// And the last one, I did not have any need for it by now, but I can imagine it could be helpful when dealing with some sort of columns and rows
// here we assume our initialization was :
// anObject = new MyObject( [1, 2, 3] );
// return [ [1,4,7], [2,5,8], [3,6,9] ]
anObject.zip([4,5,6], [7,8,9]);But there’s more, what if you want to enumerate but stop the enumeration at some point or skip an iteration ?
Prototype defines two objects for this : $break and $continue.
// Iterate over our values but break if value is 2
[1,2,3].each( function(value) {
if ( value == 2 ) throw $break;
});
// Iterate over our values but skip to next element if value is 2
[1,2,3].each( function(value) {
if ( value == 2 ) throw $continue;
});Working on Forms with Prototype
Prototype besides its nices shortcuts provides some function to work with forms elements. So let’s see what prototype can do for us.[…]