Twitter RSS

Writing your own code vs. Plugins

Tuesday, September 30th, 2008

Recently I have been discussing with some fellow coders the benefits of plugins, and when you should write your own code.  I basically wanted to throw out a couple of ideas and hope for some discussion on the topic.  Let me know what you guys think and I’ll do a follow up soon.

Some Background

I, like many of you out there, started programming back in the day by messing around with computers, writing little calculator programs, making websites, etc.  But I really didn’t get into it much more until I started college, where I immediately started programming with C++.  

Most of my classes started out with projects where we had to write everything (no STL!), and all of my code was from scratch.  Once I started doing more of the “development” in “web design and development”, I still practiced the idea that the only code I know is good is the code I write.

But now I can see…

Then one day, the skies parted, trumpets sounded, and my designer Steve said “Hey, have you heard of Ruby on Rails?”  

I immediately started developing with nearly pure Rails, using the built-in-just-about-everything and loving the fact that anything I couldn’t code (or didn’t want to) was usually readily available in plugins ( I love file_column!).  

I will say this, RoR is good code.  And it’s constantly being improved.  For the most part, the plugins are also good code.  And many times, a plugin works perfectly into your project to save you time and add functionality.  

Good Programming

Ask any real programmer, and the efficiency of code is not only about how it performs, but how long it takes you to create.  So obviously there is a trade-off between using plugins and writing your own code.  

That being said, if you are a web designer who is using rails to help you out, or just trying something new, use the plugins: they are great and it will save you days of coding and headaches.  

If you consider yourself a developer (or hope to), you should still use plugins.  If you code everything, you’re going to waste time. However, don’t completely rely on them.  If you want to be a programmer, you need to know how things work, because some day there won’t be a plugin for you, and you’re going to need to know what to do.  

Also, plugins are meant to be helpful for a range of situations.  Therefore, there is going to be some overhead.  If you are concerned with performance, and a plugin isn’t “perfect” for what you’re trying to do; you might need to code from scratch (or at least be prepared to customize the plugin code).

Here’s the thing…

All things considered, I will say that my natural tendency is to code most everything myself.  I like to know where everything is, and exactly what the code is doing.  I also appreciate when the code is only doing exactly what I want it to do.  If it’s impractical to write something, by all means I will ( and do ) use plugins.  

If you want some good coding experience, and have the time, try writing something yourself that you wouldn’t normally do.  Even if you don’t end up using it, you’ll learn a bit more about RoR (or any other language) and you may just have some good code you can use again.

Agree? Disagree? Don’t care?

I’m just one guy.  Let me know what you guys think.


Remember Me’s with Rails

Friday, September 26th, 2008

I recently had a need for a login system that needed a ‘remember me’ function. After hours of looking through countless blogs, I came to the conclusion that either (1) people don’t use a remember me function with custom authentication systems for Rails, or (2) they don’t talk about it. In this article, I outline a simple remember me system using the cookie variable in Rails that will tack on to most custom authentication systems.

Using the cookie functions in Rails is pretty straightforward. It’s used with the ActionController and is quite simple to use. Most people use sessions for authentication, which is a good idea. Sessions, unlike cookies, automatically save your content as encrypted strings using the browsers cookies. ActionController#cookie provides a method for saving information in the browser, but you need to hash the content yourself if need be.

However, if a user selects the remember me option when logging in, we would like to have the session expiration set to be a longer period, like 30 days. Unfortunately, this is quite difficult to do if you don’t want to change the expiration of ALL sessions.

My site already uses sessions for authentication, and I’m going to leave that be. In fact, I’m not going to change anything about the session variable at all. This way, I can add this remember function to almost any authentication system I use in the future very easily.

When a user is authenticated and has selected the “remember me” option, I do two things:

- create a cookie that stores (plain text) the user’s id (you can use name, email, etc. but I prefer the id because it says nothing about the user to anyone trying to get information)

- create a second cookie with an hashed string of some other information about the user( name, email, address )

if params[:rememberMe]
userId = (@user.id).to_s
cookies[:remember_me_id] = { :value => userId, :expires => 30.days.from_now }
userCode = Digest::SHA1.hexdigest( @user.email )[4,18]
cookies[:remember_me_code] = { :value => userCode, :expires => 30.days.from_now }
end

For the hashing of the second piece of information, use a hash such as SHA1 or MD5. We can use these two cookies to authenticate a user when they return after a session has expired.

if ( cookies[:remember_me] and cookies[:remember_me] and User.find( cookies[:remember_me]) and Digest::SHA1.hexdigest( User.find( cookies[:remember_me] ).email )[4,18] == cookies[:remember_me_code]  )
@u = User.find( cookies[:remember_me_id] )
session['user'] = @u.id
end

Just work that into your :before_filter for your authentication system, and you’re all set. Make sure you delete the variables when someone logs out:

if cookies[:remember_me_id] then cookies.delete :remember_me_id end
if cookies[:remember_me_code] then cookies.delete :remember_me_code end

****Edit: make sure to have “require ‘digest/sha1′” at the top of any page where you are using the SHA1 hash.


You Never Know Who’s Going to Look at Your Code

Wednesday, September 17th, 2008

In a post from the The Chromium Blog, the Google Chrome developers discussed how they decided to use webkit as their rendering engine.

WebKit became the obvious solution after talking to fellow engineers working on the Android project. They were already using WebKit (as it is a great option for mobile devices), and they trumpeted its speed, flexibility and simplicity. We routinely heard comments like “It’s so easy to hack!” and “It didn’t take me long to find my way around the code base.”

This goes to show that making well organized, beautiful code can not only reduce future and current development time, but it can also have many pleasant unexpected side effects. Who knows who might be looking at your source code in the future- potential buyers of your project, new talented team members that may otherwise choose to work elsewhere, or even your clients. Having good clean source is always a good idea, and you never know what all may come of it.


Simple login with Rails

Thursday, September 11th, 2008

We recently beta launched a new site, motionspire.com. Our main goal for the launch was to get statistics from user testing, mainly from a targeted audience. In order to keep a limited user base, we wanted to make visitors create a beta account, and use a login system in order to view the site.

I wanted to create a quick and simple login system that was, most importantly, temporary. The authentication didn’t require a lot of security because no user information was stored in our db other than an email and password. I also wanted to contain all the code for the system (as much as possible at least) in a few different directories, so I can easily remove the system for the main launch.

Here is the process I wanted to create:

  1. 1. user comes to the site, and inputs an email address requesting a beta account
  2. 2. the user gets an email with (1) a link to create their password and (2) a code for authentication
  3. 3. the user inputs the code and creates their password
  4. 4. the user can now view the site, and login at a later time

So that’s the process. My designer created two pages: (1) a page with a form requesting an account and another form to login and (2) a page to create a password with the code. I created a new controller, beta, and started with these two functions:

<span style="color: #333399;">> ruby script/generate controller beta index create_password</span>

And I created a model, betauser:

<span style="color: #333399;">> ruby script/generate model betauser</span>

The model had three fields: email, password, and a boolean ‘active’ to keep track of whether the user had activated the account. I usually use the timestamps for every model just as good practice. Here is the migration:

<span style="color: #333399;">class AddBetaUsers < ActiveRecord::Migration
def self.up
create_table :betausers do |t|
t.boolean :active
t.string :email, :password
t.timestamps
end
end</span>
<span style="color: #333399;"> def self.down
drop_table :betausers
end
end</span>

Next was the function for requesting a beta account. The form’s only input was their email:

<span style="color: #333399;">def create_beta_invite
email = params[:email]
beta = Betauser.new
</span> <em><span style="color: #999999;"> //set the account to inactive
</span> </em><span style="color: #333399;"> beta.active = false
beta.email = email
if beta.save
</span><em><span style="color: #999999;">// create a code based on the users email
// its not the most secure way to do things,
// but its simple and doesnt require another
// field in the database</span></em><span style="color: #333399;">
code = Digest::SHA1.hexdigest(email)[3,12]
</span> <em><span style="color: #999999;">
// create an email with my ActiveMailer model 'Notifier'
</span> </em><span style="color: #333399;"> email = Notifier.create_beta(email,code)
email.set_content_type("text/html")
Notifier.deliver(email)
</span> <em><span style="color: #999999;"> //redirect to the password creation page
</span> </em><span style="color: #333399;"> redirect_to :action => "create_password",
:email => email
[sourcecode language="ruby"]<span style="color: #333399;"> else
redirect_to :action => "index"
end
end</span>

[/sourcecode]

After the user gets their email with their activation code, they go to the create_password page.  I send the email as a param in the link, so the activation page will only work if they use the link in the email (or directly edit the address if they are real crafty). The create password page has 3 inputs: code, password, and passRepeat (to verify the password).  Here is the action for this form:
[sourcecode language="ruby"]<span style="color: #333399;">def make_account
@beta = Betauser.find_by_email(params[:email])
if !@beta
redirect_to :action => "index"
</span> <em><span style="color: #999999;"> //check that password fields match
</span> </em><span style="color: #333399;"> elsif params[:password] == params[:passRepeat] </span> <em><span style="color: #999999;"> </span></em>

[/sourcecode]

[sourcecode language="ruby"]<em><span style="color: #999999;"> // check that the code matches our formula from before
</span> </em><span style="color: #333399;"> if params[:code] != Digest::SHA1.hexdigest(@beta.email)[3,12]
flash[:alert] = "Incorrect Code!"
redirect_to :action => "create_password",
:email => @beta.email
else
</span> <em><span style="color: #999999;"> //save the password
</span> </em><span style="color: #333399;"> @beta.password = Digest::SHA1.hexdigest(params[:password])
</span> <em><span style="color: #999999;"> //set the account to active
</span> </em><span style="color: #333399;"> @beta.active = true
@beta.save
<span style="color: #999999;"> </span></span><em><span style="color: #999999;">//use the session cookies to log the user in,
// then redirect to the site</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> session['betauser'] = @beta.id
redirect_to :controller => "main",
:action => "index"
end
else
flash[:alert] = "Passwords do not match!"
redirect_to :action => "create_password",
:email => @beta.email
end
end
</span>

So now we have a user logged in. But we have to have a ‘gatekeeper’ function to allow logged in users to view the site, and send everyone else to the login page. We will put this in the application.rb controller:[/sourcecode]

[sourcecode language="ruby"]<span style="color: #999999;"> </span><em><span style="color: #999999;">// run the function before every action in our app</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> before_filter :check_beta
....
protected
....</span>

[/sourcecode]

<span style="color: #333399;">def checkbeta
</span><em><span style="color: #999999;">// don't check when user is in the beta pages
// that will cause an infinite redirect loop</span></em><span style="color: #333399;">
if controller_name != 'beta'
</span><em><span style="color: #999999;">// if betauser session variable is there
// and the beta user exists, set a
// global @betauser to represent the user</span></em><span style="color: #333399;">
if (session['betauser'] and Betauser.find(session['betauser']) )
@betauser = Betauser.find(session['betauser'])
</span><em><span style="color: #999999;">// if the account has not been activated,
// send the user to the create password page</span></em><span style="color: #333399;">
if @betauser.active == false
session[:original_uri] = request.request_uri
redirect_to :controller => "beta",
:action => "create_password",
:email => @betauser.email
end
else
</span><em><span style="color: #999999;">// if a user is not logged in,
// store the requested call
// and send the user to the
// account request and login page</span></em><span style="color: #333399;">
session[:original_uri] = request.request_uri
redirect_to :controller => "beta",
:action => "index"
end
end
end</span>

Notice that if the user is logged in and the account is active, there is no redirect, and the user goes to the requested page.

The only thing left is to create a login and logout function in our beta controller:

<span style="color: #333399;">def login
password = params[:password]
email = params[:email]
user = Betauser.find_by_email(email)
<span style="color: #999999;"> </span></span><em><span style="color: #999999;">// make sure the user exists</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> if !user
flash[:alert] = "No user with that email address."
redirect_to :action => "index"
<span style="color: #999999;"> </span></span><em><span style="color: #999999;">// make sure the account is active</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> elsif user.active == 0
flash[:alert] = "You must activate your beta account."
redirect_to :action => "create_password",
:email => user.email
<span style="color: #999999;"> </span></span><em><span style="color: #999999;">// verify the password</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> elsif Digest::SHA1.hexdigest(password) != user.password
flash[:alert] = "Incorrect Password."
redirect_to :action => "index"
else
<span style="color: #999999;"> </span></span><em><span style="color: #999999;">// set the session variable to log the user in</span></em><span style="color: #333399;"><span style="color: #999999;">
</span> session['betauser'] = user.id
</span><em><span style="color: #999999;">// send to the original request, if exists
// otherwise, send to the homepage</span></em><span style="color: #333399;">
if session[:original_uri]
redirect_to session[:original_uri]
else
redirect_to :controller => "main",
:action => "index"
end
end
end</span>
<span style="color: #333399;">def logout
</span><em><span style="color: #333399;"> </span><span style="color: #999999;">// set the session variable to nil
// and redirect to the login page</span></em><span style="color: #333399;">
session['betauser'] = nil
redirect_to :controller => "beta",
:action => "index"
end</span>

That’s about it. There are definitely things I could have done better, but I wanted something quick, dirty, and temporary. It works, and is not something I would want to use long term. However, if you have a need for a low-security, simple login, this gets it done. Overall, it only took me about an hour and 15 minutes to code, and will be very simple to remove.

**I will follow up with a very simple removal of the system, and post the files in their entirety at a later time. Check it out at Motionspire.com!