Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Sunday, April 22, 2007

GScript - You don't Have to Use Ruby to be Cool

So why do you think Ruby is cool?

If you are like me who work mainly on Java platform, then you probably like Ruby because how much less code you write in Ruby to achieve the same result. Now, if you think about it, not all of them are because of the type-free concept.

With the way that generics goes (what it was and what it is), we can see that the design of Java language comes with some tough trade-offs. After all, I don't suppose you want to piss off the big vendors who keep the money rolling.

Well, what if we can start fresh?

Check this one out...

var list = new ArrayList({one, two})
// this creats a map of beans with ID as key.
var idMap = list.partition(\bean -> bean.ID)
A few notes:
  • The last statement uses closure, in GScript syntax. For anyone familiar with Ruby, this call would be something like "list.partition {|bean| bean.id}"
  • The expression "bean.ID" is actually a "bean.getID()" method call, which is declared as property getter in GScript
  • Even though "list" is a type of List, you can add "Enhancement" to this interface so that you can have additional behavior built on top of existing public methods. This gives you some of the benefit of "mixing" without sacrificing the type safety. If you use apache commons library extensively, you probably have a list of enhancement that you can write already.
  • The type is inferred so you really need to type once. (Note that the type of variable idMap will be inferred as Map<Key, KeyableBean>, a generic map instead of just a Map)
  • Create the list like the way you mean it instead of the Arrays.asList syntax.
You have just caught a glimpse (and I do mean "glimpse") of GScript, the language that Guidewire developed on top of Java. You want a rule engine that can be configured? What about a rule engine that can be programed and deployed without bouncing the server?

There are talks about making this public, by which time you will be able to see the full power of it. Before that, you can consider coming in for an interview. :)

Saturday, January 13, 2007

So Close

Working on several things at the same time and they are all getting very close but just not quite. I have to say this puts a bit of stress on me.

At guidewire, I am working on a feature that involves analyzing the domain graph so that we can do things like record purging, archiving, data relationship analysis, etc. I think I have the main graph finally worked out (even have a primitive UI to show for it). I have the tests running and one of them can generate a Wiki content so that the document is always up-to-date. However, at the edge of the graph where the domain objects inside the graph has references to or from objects outside the graph, it gets a little complicated. I am keeping down a list on the wiki and it is getting more and more complete..... Just not yet.

I have arranged next sprint (two-week period) to start on next week so I was really hoping that I could finish this before I kick it off, because it will make a lot of stories easier to write. But for various reason I just couldn't quite just make it. I think I'll have to spend next Tuesday (Monday being a holiday) to make sure that the stories for the next sprint is good to go and that QA is on board with what I want to do. Adam has got me an account with Pivotal's tracker. Things are all good, just that being one-man team is not easy because you still have to do the same amount tracking if you want to still get the benefits for agile development.

At home, I have been busy with mainly two projects, BulidMaster and Selenium-Ruby.

For BuildMaster, I finally figured out how to use RubyGem to archive the file, and why reading image file with Cotta cannot read the full content (you have to call io.binmode even if you open it with 'wb' argement). I am getting very close to have Cotta support archive and zip so that we can just do 'dir.archive_to(target_file).zip' and get a zip file. I need to redesign the BuildMaster website so that project releasing, website building and cotta are presented as three separate pieces.

For Selenium, that is something that I am still not ready to share but I think it will be interesting as well. I will need to modify the selenium server and again I am getting very close.

Tuesday, January 09, 2007

This Made My Day

Enough said...

Bye bye Eclipse! I know I am supposed to say "thank you and miss you" and stuff, but I really don't want to lie.

Thursday, January 04, 2007

Domain Based Web Testing

This has been in my mind for some time and with the creation of Selenium-Ruby I finally have someplace to put it:

http://selenium.rubyforge.org/doc/domain-based-web-testing.html

The idea of domain driven is also behind the BuildMaster and Cotta project.

Thursday, November 09, 2006

Code Snippet, with Color!

I did it!

Now build master can grab code snippet from another file and apply syntax highlighting using syntax.

Next step is to figure out a way to specify the snippet validations.

I am dead tired right now and I have a 9am meeting tomorrow. So I just updated two (actually one and half) pages to show it off.


I also fixed the document source and history link at the end of each page so you should be able to see the source file now.

Saturday, September 23, 2006

BuildMaster Surgery - Moving to Cotta

I have been struggling with the File operations for a while, before I fully understand what is going on.

After I released Cotta project, the way File operations works in Ruby is like a hideous stain on top of a shining gem, always bugged me, especially when there are these nice classes like Pathname and StringIO already exist in Ruby.

Finally, my curiosity of making it better got the best of me, and I ported BuildMaster over to Cotta Style.

I have decided to call the files CottaFile and CottaDirectory so that I don't have to worried about the name space issue. Since BuildMaster does quite a bit of shell operations, I added method "shell" to the cotta instance so that the in memory version can log them for tests.

All things are happening behind the scene. The following is a code snippet demonstrate the new API:


#system implementation is injected here
cotta = BuildMaster::Cotta.new(BuildMaster::InMemorySystem.new)
file2 = cotta.file('dir2/file.txt')
file2.exists?.should_equal false
# parent directories are created automatically
@file.save('my content')
@file.copy_to(file2)
file2.exists?.should_equal true
file2.load.should_equal 'my content'
file2.read {|file| puts file.gets}


The API still need to be fine tuned but they provide all the operations that BuildMaster needs for the moment. Hopefully, someday something like this can be part of the ruby library.

BuildMaster Surgery - Moving to rSpec

This is the first part of the BuildMaster surgery that I have just finished (well, 95%) that have been keeping me up late every day. I will write the second part as a separate blog.

Moving BuildMaster to rSpec turned out to be not as smoothly as I hoped. Hopefully this blog will help anyone who is interested on similar task. This is what I have figured out so far so if there is a better way of doing it do let me know.

Syntax Change

If you have tons of test/unit scripts already, changing the code to use the rSpec syntax could be boring. They do have "Test::Unit Migration" support. However, I didn't get to take full advantage. It just generated "migration error" with no detail information for me. So I had to use some regular expressions to get the most of the change done, which I have blogged in "Going rSpec".

Specification Sharing

The "Context API" document on rSpec teaches you how to call helper methods in your specification code. However, it does not show you how you can make two context share the specifications, which is very useful if you want to make sure that two different implementation of the same interface shares the exact same behaviors.

Brain from Pivotal showed me that you can achieve this by using 'extend' (instead of include). So you can write the common behavior specifications in one file:

module CottaSpecifications

def register_cotta_file_specifications
setup do
# this one uses the @system that will be set up by each implementation spec setup
@file = BuildMaster::CottaFile.new(@system, Pathname.new('dir/file.txt'))
end

specify 'file can be created with system and pathname' do
@file.name.should_equal 'file.txt'
end
end
Then you can include those common behavior in your specification file for the implementation:
require 'cotta_file_specifications'
module BuildMaster
context 'Cotta file' do
# extending the module so that you can call the methods
extend CottaSpecifications
setup do
# setup the implementation for specifications
@system = InMemorySystem.new
end

# this call registers all the spefications
register_cotta_file_specifications
end
end
Hopefully the above code makes sense to you. If not, let me know.


Running All Specifications

Again, thanks Brain to explain the reason and provided me with a solution.

Somehow, all the registered contexts are run at the end of each context registration. So my code for loading all the tests will now make the same context run again and again:

If you have context A and B in tc_a.rb and tc_b.rb and you require each file one by one. After "require 'tc_a'", the specifications in Context A will run. After "require 'tc_b'", the specifications in Context B will run, this time ALONG with all the specification in Context A. If you have C, D, etc., then specifications of Context A will run again and again at the end of each following require.

So Brain wrote his own specification runner, which will hold off the execution until all the contexts are loaded:

require 'rubygems'
require 'spec'
#require 'diff/lcs'
dir = File.dirname(__FILE__)
#require "#{dir}/../test/common_test_case"
require 'test/unit'
Test::Unit.run = true

args = ARGV.dup
unless args.include?("-f") || args.include?("--format")
args << "--format"
args << "specdoc"
end
#args << "--diff"
args << $0
$context_runner = ::Spec::Runner::OptionParser.create_context_runner(args, false, STDERR, STDOUT)

def run_context_runner_if_necessary(system_exit, has_run)
return if system_exit && !(system_exit.respond_to?(:success?) && system_exit.success?)
return if has_run
exit context_runner.run(true)
end

at_exit do
has_run = !context_runner.instance_eval {@reporter}.instance_eval {@start_time}.nil?
run_context_runner_if_necessary($!, has_run)
end


To use this, all I needed to do was to require this file at the beginning of my "ts_buildmaster.rb" file.

And it generates nice output too:

...
Directory object with cotta for in memory system
...
- dir should return sub directory
- dir should return a directory from a relative pathname
- should get file in current directory
- should create dir and its parent
- should delete dir and its children
- should do nothing if dir already exists
- should list dirs

Directory object with cotta for physical system
...
- dir should return sub directory
- dir should return a directory from a relative pathname
- should get file in current directory
- should create dir and its parent
- should delete dir and its children
- should do nothing if dir already exists
- should list dirs
...

Monday, September 18, 2006

Going rSpec

After played with rSpec for a while, I have decided to convert BuildMaster on top of it. However, the test2spec script did not work for the project, so after a few experiment, the following regular expression helped me converting the majority of the syntax. I still had to convert the class definition to context definition manually, though.

1. Converting assert_equal(a, b) to b.should_equal a

replace "assert_equal\(([^,]+),\s*([^\n]+)\)" with "$2.should_equal $1"

2. Converting "test_..." methods to "specify ..."

replace "def test_([^\s]+)" to "specify '$1' do"

Tuesday, September 05, 2006

Speed of Unit Test

I have heard more than once from people saying that their unit tests are long because they have thousands, or tens of thousands of them.

Some would blame it on the complicated application architecture, or the language (like Java).

But if these guys can use a script language to run 132 tests, 219 assertions in just over half a second, maybe there is something to the idea of faster tests after all.

I am not saying it is easy, just that it is a goal worth pushing for.