![]() |
Articles Feed |
Categories
Archives
- July 2010 (5)
- June 2010 (4)
- April 2010 (3)
- March 2010 (2)
- February 2010 (2)
- January 2010 (1)
- December 2009 (1)
- October 2009 (2)
- September 2009 (2)
- August 2009 (1)
- July 2009 (5)
- June 2009 (2)
- May 2009 (2)
- April 2009 (8)
- March 2009 (7)
- January 2009 (2)
- December 2008 (3)
- November 2008 (5)
- October 2008 (4)
- September 2008 (6)
- August 2008 (4)
- July 2008 (5)
- June 2008 (5)
- May 2008 (4)
- April 2008 (2)
- February 2008 (4)
- January 2008 (2)
- December 2007 (2)
- November 2007 (2)
- October 2007 (2)
- September 2007 (1)
- August 2007 (3)
- July 2007 (1)
- June 2007 (4)
- May 2007 (7)
- April 2007 (2)
- February 2007 (3)
- January 2007 (3)
- November 2006 (3)
- October 2006 (3)
- September 2006 (17)
- November 2004 (1)
Craftsman Swap Day 5
by: jim | April 19th, 2009 | 2 comments »
Today turned out to be little coding and quite a bit of retrospective. I was able to pair with Nate Jackson for a little bit on the iPhone application that he’s working on. I also tested a Webrat patch on a brand new Rails application via a cucumber test to wrap that up.
Over some excellent Greek food, we had an informal retrospective. A few notes from the discussion:
- The duration of the swap seems just about right. As Dave said, the law of diminishing returns likely kicks in after a few days or a week, and the cost of another week would likely outweigh the benefits.
- Kevin Taylor brought up the idea of more planning in response to my admission that I was a bit nervous Monday morning. The source of my nervousness was really due to not knowing exactly what to expect. I think that the lack of planning was actually extremely beneficial to the flow of the week. I was able to pair with many different people, and the flow between pairs was very organic and natural. Having a plan to follow probably would have changed this dynamic for the worse.
- The feeling that the swap was successful was unanimous. We all felt that we benefitted from my being onsite for the past week. I knew that I was learning a ton and having a great experience, but I was extremely happy to hear similar feelings vocalized by the Obtivians.
This was an absolutely fantastic experience. It gave me a chance to step away from client work for an entire week and explore new technologies, techniques and ideas, work with different people, and learn a lot. I believe that both 8th Light and Obtiva have compared this time away from client work to spending a week at a conference. From my experience this past week, this was better than any conference I’ve attended. Listening to speakers talk about new ideas is one thing; actually pairing with great developers and learning hands-on is in a different league.
Craftsman Swap Day 4
by: jim | April 17th, 2009 | 1 comments »
Dave Hoover and I got back to the Mad Mimi feature implementation first thing in the morning. Dave had left a failing test the previous afternoon, which was meant to serve as a reminder for where we left off. This technique is great when you need to stop in the middle of what you’re doing, and it works well as long as you know what the next step needs to be. This should help you reestablish the context you were holding when you stopped. Of course, if you’re at a natural break point and the next step isn’t clearly defined, I tend to avoid writing a failing test. Often when I come back to code after a break, I will find a simpler way to approach the problem at hand. I don’t like short-circuiting that process by leading myself towards a solution I saw when I was leaving the code. Anyway, in this case, it was clear where we needed to go, and the failing test got us right back to it.
Mad Mimi has grown organically without any traditional marketing or advertising efforts. That is not to say that there aren’t a lot of users and a lot of data. There are a few database tables that are quite large, and optimizations have been put into place in order for the system to remain responsive. Optimizations are usually going to make a system more complex from a code standpoint. The common advice to avoid premature optimizations is valid exactly because of this. In addition, you’re almost always going to be wrong about where the bottlenecks will occur in your application. If you were to do some premature optimization, you’ll likely find yourself with overly complex code that is still slow. Mimi has been optimized only as needed, and the code base is better for it. One of the optimizations that has been made is limited use of stored procedures.
Stored procedures are not easy to test. The feature we were working on required us to pass an additional parameter into the existing stored procedure. Once we dropped into stored procedure land, we lost the safety net of our tests. So we approached the problem stepwise, in a way “test driving” the stored procedure from the user interface. Fortunately, the stored procedure isn’t overly complex, and executing a couple manual test cases made us confident that the change was correct.
After we finished the feature, it was time to deploy so the customer could test and provide feedback. The deployment process for this project is different than just about every other project I’ve ever been on. Dave has taught the customer, who is not a programmer, how to update from Subversion, run data migrations, start/restart his local web server and BackgroudRB services, etc.. With just a little help, the customer has the system running in development mode on his Mac! Not only that, the customer does the design work for the site. We just added the appropriate view files while implementing the feature with a note for the customer to come through and style the HTML and provide the correct content. The customer is heavily involved in the process, and clearly cares very much about the product. Dave is extremely lucky to have this kind of customer for this project.
Craftsman Swap Day 3
by: jim | April 16th, 2009 | 0 comments »
I spent my day pairing with Dave Hoover on Mad Mimi. The first thing I noticed during our pairing session is that Dave has many more responsibilities within Obtiva than just coding. The opportunities for distractions seem to increase exponentially the closer you are to the details of running a business. The context switching involved in handling interruptions while coding is something that I really struggle with, but Dave is quite adept at it. Having a pair helps with ignoring these distractions, for better or for worse. I’ve been using the Pomodoro technique for the past couple months with some success to try to help stay focused and deal with distractions, but I’ve found that the best way to avoid distractions is to pair program. Dave “thanked” me at the end of the day for helping him ignore his growing list of unread emails, which he would have to catch up on later. The upshot of this is that we got some concentrated work done without a ton of distractions taking our attention away from the task.
At the start of the day, we picked up a new Mad Mimi feature and started discussing it. As is usually the case when beginning to dig into a feature, we quickly came up with some questions for which we would need the customer’s input. Unfortunately, the customer wasn’t available; after making note of the questions, we started talking about a change that will be rolled out sometime in the near future, but should not be added just yet. Dave accurately identified the need to create a branch for the feature. I’ve recently had a similar need for this kind of feature branch, and I’ve done this a couple times in the past few months. Dave hadn’t created a branch in Subversion before, so here was a chance to teach him something.
There are a few different reasons why you might want to use this technique, but this is the situation I’ve been running into lately. There are (at least) two different types of branches: bug fix / release, and feature. When releasing a new version of the system, a release branch can be created in order to allow bug fixes to be quickly and safely developed and deployed. A feature branch, on the other hand, comes in handy in a number of situations. One situation is when you want to develop a new feature that needs to be deployed as an update to the release. If you were to develop the feature on the release branch, you would run the risk of making deploying the branch at a moment’s notice difficult or impossible. By developing on a feature branch, you can develop and test the feature in isolation, then merge the changes back to the release branch (and/or the trunk). In Dave’s case, he releases from the trunk, but he wanted this particular feature to not be deployed until he’s ready. A feature branch works great in this case, and he’ll only have to merge back to the trunk since there isn’t a need for a release branch. Once that merge is done, the branch can (and should) be killed; the key to keeping your branches manageable is to limit the number of branches that you ever need to think about. The goal should be for each branch to have the shortest lifetime as possible. For a decent branching strategy, see this article.
After Dave successfully created his branch (and played around with merging to understand how that would work), we went ahead and started implementing this new feature on the branch. Of course, we soon were able to get the information we needed from the customer to continue on the original (higher priority) feature. Since we were working on the branch, we didn’t need to abandon our work in order to start on the other feature. We just opened up the trunk’s code in our editor and away we went. This is another advantage of working in a feature branch: it’s simple and cheap to jump around. We spent the rest of the day working on this feature, though we ended the day without completing it. We will finish it up on day 4.
Craftsman Swap Day 2
by: jim | April 15th, 2009 | 0 comments »
Hackfest
Every week, Obtiva sets aside time for what they call Hackfest (I believe this grew out of Geekfest, which is what I’d describe as a Lunch ‘n Learn). Everyone is encouraged to take some time over lunch to work on some non-client code, and the emphasis is on “doing” instead of just talking. I really like the idea of HackFest. By encouraging employees to spend time on side projects, the company (and employee) can only benefit. Client-work is necessary in order for us to get paid, but the passion for the craft cannot flourish if the only projects one is working on is for someone else. There is something selfish about software development, and that isn’t a bad thing. In order for you to enjoy your job, I believe that you have to get satisfaction out of what you’re doing every day. I think that’s why I’m such a fan of Test Driven Development. There is always a goal, usually only a maximum of a couple minutes out: either get the current test to pass, or write a failing test (don’t forget to refactor). This constant flow is encouraging because every time you get your tests to green, there is a feeling of accomplishment.
So for Hackfest, I joined Andy Maleh on his project, which is in the really early concept phase. We worked through some mockups and designs for a social website that he has in the works. Basically, we designed and drew up paper mockups of a critical feature. Paper prototyping is intended to help developers create a user experience that meets the user’s needs. One possible mistake that you can make, as I certainly have, is to forget that you have AJAX effects at your disposal in your web application, and that your prototypes should be built with that in mind. Instead of every user input resulting in a new (paper) page being put down in front of the user, consider simulating the slide effect that would occur when you click an “add” button which dynamically inserts a new form into the page. By just replacing the entire page in front of the user, you’re really missing the user experience improvements that AJAX can give you. Andy’s mockups did this well, and I think that they will result in more successful user testing.
Afternoon Pairing
I paired with Nate Jackson for the good part of the rest of the afternoon. Nate was Obtiva’s third apprentice and is now a consultant. We were working on a really cool iPhone application, but mainly I just slowed Nate down. One thing that bothers me about developing for the iPhone is how difficult it is to test the code that touches the framework; in particular, all of the delegates in the controllers. I would like to gain a better understanding of how the framework really works so that maybe I can find some better seams for testing purposes. As a result of this inherent difficulty, we didn’t test drive the code that was written, and I noticed that the rhythm of the pairing session was distinctly different from the ping-pong sessions that I’ve grown accustomed to. Without the failing test/passing test rhythm, there aren’t distinct break points where the keyboard naturally gets passed. So though the pairing session was successful, it definitely felt different.
Craftsman Swap Day 1
by: jim | April 14th, 2009 | 2 comments »
My initial impression of the Obtiva office is the relaxed atmosphere. It consists of a big, open work area with lots of pairing stations. At the beginning of the day, I jumped right into an internal stand up meeting with a few of the Obtiva folks. It was short, sweet and to the point. One interesting difference between this and the way I’m used to doing stand ups is items that would normally be handled as followups were discussed during the individual updates. The discussions never wandered far, so this worked quite well. I was paired with Tom Kersten most of the day on some client work.
While working with Tom, I was introduced to a few tools that I hadn’t used before.
- Fluid, a Site Specific Browser that basically allows you to sandbox a web application in a dedicated browser process. You can Command-Tab to an icon that you assign, and if (or more accurately, when) your browser crashes, Fluid does not.
- GNU Screen, a window manager that removes the latency from remote pairing. Of course, you need to use an editor like vi for it to work for you.
- Text Expander, which does exactly what it says: you specify shortcuts, e.g. brb, and it will expand it to the full text, e.g. Be right back.
- Rak, a Ruby replacement for grep. It gives you the line number of the matched text, which regular grep can do too but it always takes me 5 minutes to figure out the right option to pass to get it to work.
Tom’s editor of choice (even for Rails development, which is what we were working on) is VI. I was a little worried at first, but I picked up a few tricks and it didn’t slow me down quite to a crawl like I had anticipated. We were also using Cucumber to test-drive the features we were working on; this was Acceptance Test Driven Development (ATDD) in that the test represents the behavior that the software should have. I have been test-driving my Rails code at a lower level (think model and controller), and I think that there is a ton of value in moving up to a higher level to define the behavior.
As we were implementing a feature, the following problem presented itself. We’re using Webrat in the step definitions of the Cucumber tests. The way Webrat makes you click on buttons/links is slightly problematic. You can use the text or the id of the link to tell it to click. The text is probably the least permanent part of the entire link, so by relying on the text your tests are inherently fragile. By adding an id for all the links in your application, you are cluttering up the DOM. Adding an id to all your elements is a valid solution, and one that have fallen back to in the past. But this seemed backwards; the testing is forcing us to clutter up our HTML. The testing framework should be working for us, making it easier to write valid, uncluttered HTML. Perhaps we could use the href attribute of the anchor tag to click a button? Unfortunately, Webrat doesn’t support that, but it is open-source so I ended up forking and adding this feature. However, after more discussion, I’m not convinced that this approach is indeed better than using the id approach.
One Take on Configuring Rails Routes and asset_host
by: jim | December 2nd, 2008 | 1 comments »
We recently had an interesting requirement surface. In anticipation of the release of a number of demo environments, our customer requested that system configuration be able to be done at the server level. The goal was to avoid being forced to use source control to manage configuration files.
A little background is in order. The systems that we are working on are pretty large. There are two distinct, decoupled systems, each consisting of between 10 and 40 subsystems. Of these subsystems, a handful on each side are Rails applications. Most others are small Ruby application that talk a central Rinda server. We already have a configuration strategy in place for the two systems, which involves a central configuration file per system which populates a globally-accessible configuration hash.
The directory structure looks something like this:
/deployment /deployment/system_one /deployment/system_one/etc /deployment/system_one/etc/config.rb /deployment/system_one/rails_app_one /deployment/system_one/non_rails_subsystem_one
The systemone/etc/config.rb file creates the configuration hash and populates it with all of the appropriate configurations (in practice, config.rb actually loads the appropriate configuration file based on an environment variable, i.e. RAILSENV, but we will ignore that here for brevity and clarity):
- APPLICATION_CONFIGURATION ||= {}
- APPLICATION_CONFIGURATION[:foo] = "bar"
From the context of a Rails application, this central configuration file is loaded from railsappone/config/environment.rb:
- # Load the system configuration
- require File.expand_path("/deployment/system_one/etc/config")
- # Bootstrap the Rails environment, frameworks, and default configuration
- require File.join(File.dirname(__FILE__), 'boot')
With this configuration strategy in place already, the team was tasked with setting up multiple demo environments for the entire system, each deployed on a different server and accessed through a different external URL. We could have attempted to solve the configuration issues by just adding many separate Rails environments, say demo1 and demo2, but there were a couple problems with that. First, the configurations for the different environments would have been nearly identical. Second, we wanted to avoid the complexity of relying on source control to manage the configurations for each deployment. In most cases, relying on the Rails convention of setting up a new environment makes a lot of sense. However, in our case, the only differences between the environments would be setting the asset_host and routes. Storing this in source control means that in order to make a change to the external URL means that files under source control need to be modified, checked in and then redeployed to the affected system. It makes much more sense to have some reasonable defaults in source control and then provide a mechanism to override these configurations at the server level.
The solution to this problem ended up being quite simple. We first agreed on an acceptable location for the server configuration file:
deployment/config deployment/config/server_config.rb
In order to allow for overriding the system configuration, this code was added to /deployment/system_one/etc/config.rb:
- # Allow for configuration to be overridden by a config file that is not under source control
- server_config_file = File.expand_path("/deployment/config/server_config.rb")
- require server_config_file if File.exists?(server_config_file)
The server configuration in deployment/config/serverconfig.rb will be loaded (and executed), if it exists. If it doesn’t exist, the default configurations will be used, which is the desirable behavior. The first task was to configure the assethost. To start with, we add the desired assethost configuration to /deployment/config/serverconfig.rb
- APPLICATION_CONFIGURATION[:my_rails_app_asset_host] = "https://www.example.com/my_rails_app/demo"
For each Rails environment that we want the assethost to be configurable from the server configuration file, just add this line to the appropriate config file, e.g. deployment/systemone/railsappone/config/environments/demo.rb:
- config.action_controller.asset_host = APPLICATION_CONFIGURATION[:my_rails_app_asset_host]
You would want to add a separate assethost configuration for each Rails application, and each Rails application would set its own assethost to the appropriate configuration. For a simple configuration like asset_host, this works great. For routes, though, it gets a bit more complicated. We need a Mapper instance in order to build a route. For example, your routes configuration looks something like this:
- ActionController::Routing::Routes.draw do |map|
- map.connect ':controller/:action/:id'
- end
We initially saw two options. 1) Create a data structure representing the desired routes, store it in the configuration, then from the routes file iterate through the structure created in the configuration file, creating the appropriate routes; or 2) Store some Ruby code in the configuration so that the context (i.e. the Mapper instance) can be passed to the block at the run-time. This second option eliminates the need for a secondary data structure to represent the routes. How better to configure the routes than with the actual code used to configure them?
It turned out that this was nearly as simple as the assethost configuration. First, let’s define the routes in /deployment/config/serverconfig.rb:
- APPLICATION_CONFIGURATION[:my_rails_app_routes] = lambda do |map|
- map.connect 'my_rails_app/demo/:controller/:action/:id'
- map.connect 'my_rails_app/demo', :controller => "login", :action => "index"
- map.login 'my_rails_app/demo/login', :controller => "login", :action => "index"
- end
We have used the lambda method to convert a block into a Proc object. The Proc object is stored as a value in a hash which can be executed later. The rest should look familiar to anyone using Rails; it is exactly what we would have had in our routes file. Now, from /deployment/systemone/railsapp_one/config/routes.rb, we can do this:
- ActionController::Routing::Routes.draw do |map|
- APPLICATION_CONFIGURATION[:my_rails_app_routes].call(map) if APPLICATION_CONFIGURATION[:my_rails_app_routes]
- # Install the default route as the lowest priority.
- map.connect ':controller/:action/:id.:format'
- end
In order to invoke the Proc object stored in serverconfig.rb, we just send the call message to the Proc stored in the configuration hash, if it exists. In our implementation, we invoke the Proc configured in serverconfig.rb before all of the other (default) routes, with the assumption that the configured routes should have the highest priority. If we run into a case where this isn’t the case, we can address that problem then.
How Jim got started programming
by: jim | October 6th, 2008 | 0 comments »
Tagged by Micah Martin, Paul Pagel, and Eric Smith.How old were you when you started programming?
I took an introduction to programming college class when I was a sophomore in college, so 18 years old.How did you get started programming?
I was looking for a new major after deciding chemistry (and lab work in particular) probably wasn't for me. So I decided to try a few different classes, hoping to find something that interested me. I took an accounting, an economics, and a programming class. Luckily, I got hooked on programming instead of becoming an accountant.What was your first language?
JavaWhat languages have you used since you started programming?
Java, C++, Perl, JavaScript, Python, C#, ColdFusion, VB 6.0, VB.NET, VBA, PHP, Ruby, Objective-CWhat was the first real program you wrote?
Late in my sophomore year, I was fortunate and landed a job with the US Geological Survey (USGS) in Urbana, IL. At this point, nearly all of the programming I had ever done were machine problems for my CS classes. I was thrown into a whole new world of web programming, tasked with writing CGI scripts with this weird looking language called Perl. My first project was to write an application to disseminate groundwater data to Illinois residents visiting this site. This was all done using Perl to dynamically generate horrendous HTML.What was your first professional programming gig?
Web developer for USGSIf there is one thing you learned along the way that you would tell new developers, what would it be?
You don't know as much as you think you do. Even the best developers are constantly learning and working to improve their craft. To do this, you need to read about technologies that are new to you and then practice, practice, practice. You cannot limit your learning to between 9 and 5.What’s the most fun you’ve ever had programming?
It probably sounds passe by now, but the project that I and many of the 8th Lighters are currently working on has been the best professional experience I have ever had. Never have I worked with a group of people so concerned about and cognizant of the quality of the work and the success of the project. Software is a people business, and working with great people makes even the most mundane tasks bearable and the most difficult challenges surmountable. Next up: Eric Meyer and Matt Segvich.Rinda 101
by: jim | February 11th, 2008 | 2 comments »
Background
When building a software system composed of multiple decoupled components, the need typically arises for interprocess coordination and communication. As an example, say that we have a customer requirement that allows emails to be sent based on certain events or conditions that occur while the user is interacting with a web application. We build an email component that takes care of the details of actually sending of the email. The web application could use the email component directly, but that would introduce a dependency that we’d rather avoid. How can the two processes communicate without creating a dependency between the two?
If you’re using Ruby, there’s an option that is built right into the standard library. The Rinda module implements the Linda distributed computing paradigm in Ruby. That’s great, but what is Linda? Linda is a coordination language developed by a couple of Yale researchers in the early 1990s. Linda itself is built on the concept of a tuplespace. A tuple is an ordered list of objects, and a tuplespace contains a set of tuples which can be accessed concurrently. A basic set of operations were defined by the language to allow reads, writes and takes.
Getting back to the example, how can Rinda be utilized to enable communication between the web application and the email component? First, a tuplespace will need to be setup that both components can access. When a condition arises within the web application that requires an email to be sent on its behalf, a specific tuple will be written to the tuplespace. The email component is looking for tuples that match a specific pattern. When it finds the tuple written by the web application that matches this pattern, it processes the request and reports back to the tuplespace when finished. In this way, we end up with only data coupling between the two processes, which is a much looser coupling than a direct dependency. This type of approach bears some resemblance to a blackboard system.
Now that we know a little bit about what Rinda can buy us, let’s dive in and try it out.
Hello World
Let’s put together a simple server and client to demonstrate how each works.
Here’s a simple server (server.rb):
- require "rinda/tuplespace"
- port = 4000
- ts = Rinda::TupleSpace.new
- DRb.start_service("druby://:#{port}", ts)
- puts "Rinda listening on #{DRb.uri}..."
- DRb.thread.join
We start by setting the default port to 4000. We then create a new TupleSpace object. The call to start_service starts a local dRuby server listening on port 4000. Passing the TupleSpace as the second parameter sets the server’s front object to the TupleSpace. The URI of the server is output, and the program waits for a kill signal, and we have a Rinda server ready to be accessed by a client. To start the server:
$ ruby server.rb
Here’s a simple interactive client (client.rb). It allows input to be gathered on standard input, with the form
- require "rinda/rinda"
- include Rinda
- port = 4000
- ts = DRbObject.new(nil, "druby://:#{port}")
- while message = gets
- begin
- args = message.split(" ")
- method = args.shift.to_sym
- topic = args.shift.to_sym
- message = args.shift
- message = nil if message == "nil"
- tuple = [topic, message]
- puts ts.send(method, tuple)
- rescue Exception => e
- puts e.message
- end
- end
We’re using the send method in order to invoke the method specified on standard input. There’s also a conversion between nil as a string into nil proper. This is necessary because of wildcards, which we’ll get to in a minute. With the server running, run the client to start a new interactive session:
$ ruby client.rb
Below is a sample session (output is indented for clarity):
write message hello Rinda::TupleEntry:0x4f998 write message world Rinda::TupleEntry:0x4cfa4 read message nil message hello take message nil message hello take message nil message world
The inputs above translate to these method calls:
ts.write([:message, "hello"]) => Rinda::TupleEntry:0x4f998 ts.write([:message, "world"]) => Rinda::TupleEntry:0x4cfa4 ts.read([:message, nil]) => "hello" ts.take([:message, nil]) => "hello" ts.take([:message, nil]) => "world"
Note that reads do not remove the tuple from the tuplespace. The tuple is left untouched such that other clients can access the same data. This is an important concept when dealing with synchronization that we’ll return to in a future post.
The structure of the patterns used in the read and take methods deserve some additional attention. When the tuplespace is queried through a read or a take, there are two criteria used to determine if a tuple matches. First, the length of the tuple must match the pattern’s length. In the example above, all patterns are pairs (2-tuples), so only tuples that are pairs will qualify. Second, the tuple must match the pattern specified. When specifying a pattern, nil is used as a wildcard, which will match anything.
Assume that our tuplespace has the following tuples:
1) [:message] 2) [:message, "hello"] 3) [:message, "hello", "world"]
If the pattern [nil] is specified, e.g. ts.read([nil]), only #1 will match.
If the pattern [:message, nil] is specified, only #2 will match. Of course, [:message, “hello”] would also result in #2 matching as well, while [:message, “world”] would not.
Using [:message, nil, nil], [:message, “hello”, nil], or [:message, nil, “world”] would all result in #3 matching. So it is important to remember that patterns are matched on both length and content of the tuple.
Conclusion
We haven’t even scratched the surface of what can be done with Rinda. This introduction only gets the simplest client and server talking to each other. Stay tuned for more.
Lending a Hand
by: jim | August 21st, 2007 | 0 comments »
At Agile 2007, we put on a workshop called RailsFest. The original intent was to provide a one-day session that conference attendees could visit to get an opportunity to work on a Ruby on Rails project. What that project was to be, we didn’t really know. In the way that things sometimes work out, we ended up with the opportunity to work all week with a non-profit organization that has touched more lives than most of us ever will.
The organization that we were working with is called Mano a Mano. Their mission is to “improve the health and well-being of impoverished Bolivians.” Segundo Velasquez, president of Mano a Mano, was our on-site customer, and I was lucky enough to work with him for part of the week. At first, he was a little unsure of his role in the process, but by the end of the first afternoon he had become comfortable in expressing what he wanted the software to be. It was a lot of fun watching Segundo learn about and adapt to the approach that we were taking in creating software for his organization.
Mano a Mano works in a very different way than most other charity organizations. Though they provide aid to the economic poor in Bolivia, it is not a free handout. The intent of the whole process is to bootstrap poor communities. Segundo told me of a specific project where they were able to increase the per capita income by 100% (from $150 to $300) the first year. By helping to provide the basic necessities that we often take for granted (e.g. potable water, roads to allow for the transportation of goods and people, etc.), the citizens were able to stop worrying about these basic things and concentrate on improving the lives of themselves, their families and their community.
Though RailsFest ended last Friday, there is still work to be done. If you are interested in giving back a little bit through your craft, please drop me a line at jim (at) 8thlight (dot) com.
Sharing a Database Among Multiple Rails Applications
by: jim | February 20th, 2007 | 4 comments »
A sign of the success of Ruby on Rails is the size of applications being written with it. Currently, we are working on a project with many interconnecting modules that we split up into separate Rails applications. However, the business domain was the same among the projects, resulting in the different projects sharing the same database. This article discusses the way we’ve accomplished this.
Create a Lib Project
We first need a place to pull our common classes into. This will not be a Rails application; it will just be a Ruby module that your Rails application will require. Our directory structure will look something like this:
/projects/my_lib --> lib --> spec
Now we can move on to the actual refactoring.
Pull the Models Out
The first step is the easiest: pull out the models from the app/models directory of your Rails application into the lib directory of your new library. In the config/environments.rb file within your Rails application, you will need to add this new directory to your Ruby search path by adding this line to the end of the file.
- $: << File.dirname(__FILE__) + '/your_lib_path'
More on the Ruby search path later.
Pull the Tests Out
Let’s move on to making our specs pass. Now that we don’t have the Ruby environments loaded up as part of our test helpers (spec_helper for the rspecers out there), we need to establish an Active Record connection ourselves. Create a spec helper for our lib specs and start it with the connection to the database like so.
- require 'rubygems'
- require 'active_record'
- ActiveRecord::Base.establish_connection(
- :adapter => "postgresql",
- :host => "localhost",
- :username => "rails",
- :password => "",
- :database => "<db name>_test"
- )
This is the same way Rails sets up your database connections for active record when you load the Rails environment. Now your specs just need to require this new spec_helper in order to get Active Record functioning.
Use Models Across Projects
Now you are free to use the same database across many projects. To do this, you just need to either require a single lib file that loads all the models, or you could put the lib directory in your Ruby search path.
Ruby Search Path vs. Lib Require File
The new common library will need to be included in your rails application. There are two methods we have used to do this. The first is to create a library file that does nothing more than requires all of the common lib files, looking something like the following.
- require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_a'
- require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_b'
- require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_c'
- require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_d'
- require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_e'
The problem with this approach is it requires you to sequence the require statements in the appropriate order, or else you will get errors about missing objects. The benefit is that you load the entire environment in one place rather than adding require statements at the top of each file to include the needed external dependencies. These code files that do nothing but require files are no stranger to Ruby; however, they must be managed properly as they become temperamental and irritable when dependencies are mixed up.
The other approach we have used is what I recommended above. In your Rails application’s environment, add the lib directory to your search path, and include the require statements as needed in each model file. Of course, by including one file you will be implicitly including all of the dependencies. Using this technique, you can eliminate files that just require other files.
#