Students often ask me for Ruby on Rails books that I could recommend.
There are two Rails books that I recommend:
Agile Web Development wit Rails (if you start with Rails)
Ruby for Rails (once you know Rails)
However, there are some books that are not about Rails, but I think that every Rails programmer should read:
Refactoring - Martin Fowler
This book teaches you what is elegant code and how easily you can change your existing code into something beautiful :) It also teaches many OOP concepts.
The Pragmatic Programmer: From Journeyman to Master - Andrew Hunt and David Thomas
Lots of useful techniques every programmer should know. Testing, debugging, version control etc.
Peopleware - Tom de Marco
It's not technology that matters. It's people. Read it before you start any bigger project.
Death march - Edward Yourdon
Why projects fail. What to ask for before you join a new team.
What other books would you include in this list?
Monday, December 6, 2010
Friday, May 14, 2010
Rails MVC: How to deal with fat models
My presentation on the last DRUG (Wroclaw Ruby User Group, D stands for Dolnoslaskie) about Rails fat models.
I covered some of the techniques we use in our projects:
I covered some of the techniques we use in our projects:
- current_user is never nil
- a class for each user type - User, Guest, Admin, SuperAdmin, Partner
- remove "if" statements from views
- minimize the number of "if" statements in controllers and models
- modules to extract common behavior from models
- one controller action calls only one model method
- in controllers everything goes through current_user
- using custom exceptions for Model - Controller communication
Thursday, April 15, 2010
Greasemonkey as a temporary solution for a rewrite project
We are starting a new project for a client. This client has a suite of websites around their main internet product. Our job is to rewrite one of the existing websites.
The thing is that until we are fully ready with the rewrite we can't release our software for production use. It means that one of the main use cases (scenario which starts on the main product and leads the user with some data to our tool) is not easily possible to be manually testable.
The way the main product brings users to our tool is simple - by using a link with some parameters. We need to parse the parameters and let the user use the imported data.
As a temporary solution we decided to create a simple Greasemonkey script.
In our case we need to replace all the links pointing to the old tool. The links should now point to the new url. Here is the full code we needed:
// ==UserScript==
// // @name url modifier
// // @include http://*.example.com/*
// // ==/UserScript==
//
function replace_import_link(e) {
var href = this.getAttribute("href");
new_href = href.replace("http://example.com/old_tool", "http://example.com/new_tool");
this.setAttribute("href", new_href);
}
var allLinks = document.getElementsByClassName('old_tool_link');
for (var i = 0; i < allLinks.length; i++) {
link = allLinks[i];
link.addEventListener('click', replace_import_link, true)
}
As you see, we create a click event handler in which we replace the existing href attribute.
Possibly, there were other solutions, but this one was quite simple and seems to work fine for us. Having this script, we can test the full importing scenario even though we are not ready for production release yet.
Do you have any other idea how to solve this kind of problems with a rewrite project?
BTW, it's not our first rewrite to Rails :) Our biggest rewrite so far was http://restaurant-kritik.de. Restaurant Kritik was rewritten from PHP to Rails. This time we work on a website which was created using Flash. In both cases we support migrating data from existing databases.
The thing is that until we are fully ready with the rewrite we can't release our software for production use. It means that one of the main use cases (scenario which starts on the main product and leads the user with some data to our tool) is not easily possible to be manually testable.
The way the main product brings users to our tool is simple - by using a link with some parameters. We need to parse the parameters and let the user use the imported data.
As a temporary solution we decided to create a simple Greasemonkey script.
For those of you who don't know what is Greasemonkey - it's a tool which lets your browser change the html code on-the-fly. You write a JavaScript snippet of code which is run as if it was part of the webpage JavaScript.
In our case we need to replace all the links pointing to the old tool. The links should now point to the new url. Here is the full code we needed:
// ==UserScript==
// // @name url modifier
// // @include http://*.example.com/*
// // ==/UserScript==
//
function replace_import_link(e) {
var href = this.getAttribute("href");
new_href = href.replace("http://example.com/old_tool", "http://example.com/new_tool");
this.setAttribute("href", new_href);
}
var allLinks = document.getElementsByClassName('old_tool_link');
for (var i = 0; i < allLinks.length; i++) {
link = allLinks[i];
link.addEventListener('click', replace_import_link, true)
}
As you see, we create a click event handler in which we replace the existing href attribute.
Possibly, there were other solutions, but this one was quite simple and seems to work fine for us. Having this script, we can test the full importing scenario even though we are not ready for production release yet.
Do you have any other idea how to solve this kind of problems with a rewrite project?
Thursday, March 4, 2010
Google Spreadsheet as an admin panel for your web app
Today, I would like to share a story with you. A story which led us to discover a nice and simple technique for building admin panels.
The customer's request
Recently, in one of our projects (e-commerce), we've had the following requirement:
At first it seemed strange to me. Why would we want to edit prices using Excel if it can easily be done using the web admin panel which we created. I asked the customer what's the reason and he came up with a fair argument, that it's much easier to mass-edit prices using formulae or using copy/paste in Excel.
I was convinced at this point, but then Yashke (my teammate) suggested that maybe it's better if we do the same, but with Google Spreadsheets (GS), hopefully saving the time for exporting/importing from Excel. The idea sounded pretty cool, so I asked the customer if it was acceptable and I was given a positive response.
The setup we are trying
As always with this kind of risky situations (new technology) we prepared the simplest solution that could possibly work and exposed it in the admin panel. After some discussions we decided for the following workflow.
As you can see, we're not relying fully on Google Spreadsheets - it's just a tool for editing the data. We still have the traditional web-based admin panel. So the admin logs in to the admin panel and then he can generate a spreadsheet with some data.
This project is not finished yet, but the first feedback is very positive.
The advantages
Our journey with GS started with a single requirement, but over time we discovered many exciting opportunities, like:
The customer's request
Recently, in one of our projects (e-commerce), we've had the following requirement:
In order to edit prices
As an admin
I want to export products to Excel, edit them and upload back
At first it seemed strange to me. Why would we want to edit prices using Excel if it can easily be done using the web admin panel which we created. I asked the customer what's the reason and he came up with a fair argument, that it's much easier to mass-edit prices using formulae or using copy/paste in Excel.
I was convinced at this point, but then Yashke (my teammate) suggested that maybe it's better if we do the same, but with Google Spreadsheets (GS), hopefully saving the time for exporting/importing from Excel. The idea sounded pretty cool, so I asked the customer if it was acceptable and I was given a positive response.
The setup we are trying
As always with this kind of risky situations (new technology) we prepared the simplest solution that could possibly work and exposed it in the admin panel. After some discussions we decided for the following workflow.
- Admin presses "Generate the spreadsheet" in the web admin panel
- Then he goes to the google spreadsheet page
- He edits the prices
- He goes back to the admin panel
- He clicks "import prices"
As you can see, we're not relying fully on Google Spreadsheets - it's just a tool for editing the data. We still have the traditional web-based admin panel. So the admin logs in to the admin panel and then he can generate a spreadsheet with some data.
This project is not finished yet, but the first feedback is very positive.
The advantages
Our journey with GS started with a single requirement, but over time we discovered many exciting opportunities, like:
- mass-editing records
- sorting
- search
- search & replace
- using formulae
- using colors
- charts!
In the project I mentioned above, we use it for the following features:
All of these things would be more difficult if we did it in a traditional web admin panel.
Another thing where Google Spreadsheet could be useful is for rapid prototyping. We already have some code that let us easily take a collection of objects and display them in a worksheet, so reusing it will be very easy.
The drawbacks
There are some drawbacks of course:
1. Speed
- mass-editing prices for product variants (we have about 2000 products, each of them can have more than 300 variants, that's a story for another blog post - how assigning prices can be challenging)
- selecting which products to show on the main page, in what order etc.
- managing other kind of collections, like bestseller products
- displaying orders, so that it can be easily sorted, grouped. It's also useful for any kind of reports.
All of these things would be more difficult if we did it in a traditional web admin panel.
Another thing where Google Spreadsheet could be useful is for rapid prototyping. We already have some code that let us easily take a collection of objects and display them in a worksheet, so reusing it will be very easy.
The drawbacks
There are some drawbacks of course:
1. Speed
Exporting 2000 records to GS takes about 1 minute. The good thing is that they start appearing as soon as you press "Export", so you can see the first rows quickly.
The same problem is with importing. It's slow.
2. Relying on Google
We don't know if Google Spreadsheets will live forever, so relying so hard on it may be risky. We try to keep the GS layer simple, so that at any point we can change the implementation to handle Excel or Resolver One files.
Conclusion
The same problem is with importing. It's slow.
2. Relying on Google
We don't know if Google Spreadsheets will live forever, so relying so hard on it may be risky. We try to keep the GS layer simple, so that at any point we can change the implementation to handle Excel or Resolver One files.
Conclusion
So far, our experience with GS is very positive. As we build many web applications for which the admin panel plays an important role, we already consider using GS in other projects, like social network apps (statistics data) or a web surveys app (collecting and analyzing the survey results).
We use exclusively Rails for our web apps, but the idea of using GS can be applied with any other technology.
Let me know if you have any specific questions regarding the topic of using GS as the admin panel. I will be happy to help you.
Friday, October 24, 2008
REST: Some tips and implementing "Forgot your password?"
If someone asked me what was the best thing I had learnt over the last 12 months, I would say: REST.
I know that the RESTful approach requires changing our way of thinking, and it took me a while to "switch". The benefits are huge. From my experience the main advantages of REST are:
One of the examples that is hard/not worth (according to some people) to implement in a RESTful way is "Forgot your password" functionality. Some people feel like it could be just "/users/4/reset_password" and that we could use a custom action for the users controller. I know that it can work, but I see no reason to break the REST rules here (by adding a non-REST action to the controller).
My RESTful solution
The feature should let users provide an email, send them a link to a page, where they can change their password.
We have several steps to follow:
Given these steps, I split the code into the following controllers and actions:
Email Verification
We want to let people create a new Email Verification, so:
I created an EmailVerificationsController with new/create actions.
It is responsible for:
After veryfing the email, we want people to change (update) their Password, so:
PasswordController with edit/update actions
it's responsible for:
That's it.
Techniques
This example shows that a controller doesn't have to reflect a ActiveRecord class. There is no such model as EmailVerification in my code. It could be worth considering it, but in my case I don't think it was needed.
A useful technique for REST-izing your design is to turn verbs into nouns, thus making them easier to expose as resources. In this case, we wanted to verify an email, so I turned it into "creating an EmailVerification resource".
Sometimes changing the words slightly can help. Instead of resetting passwords, I use "update a Password".
As always, I try to move as much logic to the model as I can. Ideally I just call one model method and handle exceptions if there are any. See this article for more details about this approach.
A controller can use other models. In our case the PasswordsController could call User.update_attributes method to change the password.
Bonus
A friend of mine asked recently how I'd implement a feature, that let's an admin making another user a superuser.
My thinking here is that I turn the "making someone a superuser" into "creating a new superuser". This shows me that we now have a resource called Superuser. According to REST a Create action expects the params to be sent using POST. So we can just POST the id of the user to the Create action. In the Create action we just call admin.make_superuser(params[:id]).
Questions
What is your opinion about these solutions?
Would you solve them differently?
Feel free to ask me how I'd RESTfully implement other features.
I know that the RESTful approach requires changing our way of thinking, and it took me a while to "switch". The benefits are huge. From my experience the main advantages of REST are:
- the consistency it brings to the app,
- a standardized way of creating controllers,
- thinking always in terms of resources - what is the 'thing' that the user wants to create/update/destroy?
- a higher-lever abstraction that simplifies communication with other people in the team
- very often there's only one RESTful way of implementing a certain feature
- consistent API, in one project we use Flex for the UI, and being RESTful makes it very easy to work with Rails controllers.
One of the examples that is hard/not worth (according to some people) to implement in a RESTful way is "Forgot your password" functionality. Some people feel like it could be just "/users/4/reset_password" and that we could use a custom action for the users controller. I know that it can work, but I see no reason to break the REST rules here (by adding a non-REST action to the controller).
My RESTful solution
The feature should let users provide an email, send them a link to a page, where they can change their password.
We have several steps to follow:
- Show a page where people can type their email, verify the email
- Generate a reset_password code.
- Send an email with this link.
- Clicking on this link should check if it's a valid reset_password code
- If it is valid, then show a page where a user can type the new password twice.
- Display a message appropriate for the result (error when the passwords don't match etc.)
Given these steps, I split the code into the following controllers and actions:
Email Verification
We want to let people create a new Email Verification, so:
I created an EmailVerificationsController with new/create actions.
It is responsible for:
- finding the user by email,
- generating the reset_password code,
- sending the email,
- handling errors
After veryfing the email, we want people to change (update) their Password, so:
PasswordController with edit/update actions
it's responsible for:
- authenticating the user by the reset_password code,
- letting the user change the password,
- displaying errors.
That's it.
Techniques
This example shows that a controller doesn't have to reflect a ActiveRecord class. There is no such model as EmailVerification in my code. It could be worth considering it, but in my case I don't think it was needed.
A useful technique for REST-izing your design is to turn verbs into nouns, thus making them easier to expose as resources. In this case, we wanted to verify an email, so I turned it into "creating an EmailVerification resource".
Sometimes changing the words slightly can help. Instead of resetting passwords, I use "update a Password".
As always, I try to move as much logic to the model as I can. Ideally I just call one model method and handle exceptions if there are any. See this article for more details about this approach.
A controller can use other models. In our case the PasswordsController could call User.update_attributes method to change the password.
Bonus
A friend of mine asked recently how I'd implement a feature, that let's an admin making another user a superuser.
My thinking here is that I turn the "making someone a superuser" into "creating a new superuser". This shows me that we now have a resource called Superuser. According to REST a Create action expects the params to be sent using POST. So we can just POST the id of the user to the Create action. In the Create action we just call admin.make_superuser(params[:id]).
Questions
What is your opinion about these solutions?
Would you solve them differently?
Feel free to ask me how I'd RESTfully implement other features.
Monday, July 7, 2008
MVC: How to write controllers
I've been working on many projects (web and desktop applications), and I must say that the way MVC is implemented varies a lot. That's probably a good thing, since every project is different. I believe, however, that there are some common patterns that could improve most of the MVC applications.
1. Keep it simple
Don't create additional tiers unless they are needed. Every class should clearly belong to either Controller, Model or a View.
2. Thin controller, fat model
This is probably the most useful lesson I learnt from the Rails world. I'm not going to elaborate on that here, as it was covered in many other places. Just move all your logic to models. It's that simple.
3. RESTful design
If you work on a web application, and still don't know what REST is, then go and read about it. It's easy and it will simplify your design a lot. Basically, it makes you think about your app as a set of resources. Every action in your controllers does one of the following with your resources: new, create, edit, update, show, destroy, index(list). At first, it can feel as a limitation, but it is the kind of limitation that actually helps you.
Many people argue that not everything can be RESTified. I agree that there are places where REST is not needed, however it's always a good exercise to find a RESTful solution.
4. Communication between controllers and models
As always, when you delegate some work further, you need to control the result somehow. The same happens when you move all the logic from controllers to models.
There are three ways (that I'm aware of) of implementing the communication between a controller and a model.
4a. Return codes
You use return codes when your model methods return not the data, but the result of an operation.
An example here is ActiveRecord::Base.save method which returns true when the save was successful, false otherwise.
In my opinion it's a good solution for simple situations. However, I don't like the "if" condition here and also it can go messy with more complex examples.
4b. Ask the object for its state
4c. Don't ask, tell. Use custom exceptions
That's my favorite, because it eliminates all the "if"s from my controller code. I know it's just a syntactical change, but I prefer to see something like this in my controller, rather than the example above:
Here we use custom exceptions to define a "protocol" for the communication between a model and a controller. Obviously we need to define the exceptions somewhere. I like to add them at the top of the associated model class:
So, what are your favorite patterns for writing controllers?
1. Keep it simple
Don't create additional tiers unless they are needed. Every class should clearly belong to either Controller, Model or a View.
2. Thin controller, fat model
This is probably the most useful lesson I learnt from the Rails world. I'm not going to elaborate on that here, as it was covered in many other places. Just move all your logic to models. It's that simple.
3. RESTful design
If you work on a web application, and still don't know what REST is, then go and read about it. It's easy and it will simplify your design a lot. Basically, it makes you think about your app as a set of resources. Every action in your controllers does one of the following with your resources: new, create, edit, update, show, destroy, index(list). At first, it can feel as a limitation, but it is the kind of limitation that actually helps you.
Many people argue that not everything can be RESTified. I agree that there are places where REST is not needed, however it's always a good exercise to find a RESTful solution.
4. Communication between controllers and models
As always, when you delegate some work further, you need to control the result somehow. The same happens when you move all the logic from controllers to models.
There are three ways (that I'm aware of) of implementing the communication between a controller and a model.
4a. Return codes
You use return codes when your model methods return not the data, but the result of an operation.
An example here is ActiveRecord::Base.save method which returns true when the save was successful, false otherwise.
order = Order.new(params[:order])
if order.save
flash[:message] = "The order was created!"
redirect_to order
else
flash[:errors] = "Something was wrong"
redirect_to new_order_path
end
In my opinion it's a good solution for simple situations. However, I don't like the "if" condition here and also it can go messy with more complex examples.
4b. Ask the object for its state
This is slightly better than a), because the return code knowledge is hidden in method calls, but we still see the problem of many "if" statements.
credit_card = CreditCard.new(params[:credit_card])
if credit_card.valid?
if order.capture_payment(credit_card)
flash[:message] = "success"
else
flash[:message] = "payment failure"
end
else
flash[:message] = "credit card validation error"
end
redirect_to order
4c. Don't ask, tell. Use custom exceptions
That's my favorite, because it eliminates all the "if"s from my controller code. I know it's just a syntactical change, but I prefer to see something like this in my controller, rather than the example above:
begin
order.pay(credit_card)
flash[:message] = "success"
rescue CreditCardNotValid
flash[:message] = "credit card validation error"
rescue PaymentFailed
flash[:message] = "payment failure"
end
Here we use custom exceptions to define a "protocol" for the communication between a model and a controller. Obviously we need to define the exceptions somewhere. I like to add them at the top of the associated model class:
class CreditCardNotValid < Exception
class PaymentFailed < Exception
class Order < ActiveRecord::Base
def pay(credit_card)
...
end
end
So, what are your favorite patterns for writing controllers?
Thursday, June 26, 2008
Git: working with branches
The way we work with git is that for every remote pair programming session we create a separate branch. We give it a name after the feature we're working on.
It automatically switches to this branch.
During our work we tend to checkin our changes quite often. We do it with:
After we finish our session, we do two things.
First, we merge our branch to master:
Then, we delete the branch we were working on:
That's it.
If you happen to delete the branch BEFORE you merge it, don't panic, there is a solution.
In order to undelete a branch just after you deleted it, do:
git checkout -b feature1
It automatically switches to this branch.
During our work we tend to checkin our changes quite often. We do it with:
git checkin -a -m "created some specs for class A"
After we finish our session, we do two things.
First, we merge our branch to master:
git checkout master
git merge feature1
Then, we delete the branch we were working on:
git branch -D feature1
That's it.
If you happen to delete the branch BEFORE you merge it, don't panic, there is a solution.
In order to undelete a branch just after you deleted it, do:
git checkout -b new_branch_name HEAD@{1}
Subscribe to:
Posts (Atom)