{"id":2263,"date":"2011-09-20T16:19:43","date_gmt":"2011-09-20T19:19:43","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=2263"},"modified":"2011-09-20T22:17:12","modified_gmt":"2011-09-21T01:17:12","slug":"bare-bone-stripped-down-devise","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2011\/09\/bare-bone-stripped-down-devise\/","title":{"rendered":"Bare-bone, stripped-down Devise"},"content":{"rendered":"

Last week I spoke at Silicon Valley Ruby Group about PlataformaTec’s open source tools, mainly Devise<\/a>, Simple Form<\/a> and Responders<\/a>.<\/p>\n

When talking about Devise, I’ve mentioned that, before creating Devise, we were alternating between using Authlogic<\/a> or Clearance<\/a> in our projects. However, we soon realized that we needed a solution that was as customizable as Authlogic (allowing us to choose behaviors and several configuration options) and as complete as Clearance (whole MVC stack). It is fun to remember this happened more than 2 years ago.<\/p>\n

After the presentation, someone came to talk to me about Sorcery<\/a> and said it would be nice if Devise provided the same kind of tooling, allowing someone to build their own controllers and views around Devise instead of using Devise built-in controllers and views. His proposal surprised me, because this approach is totally possible with Devise and it was one of our design goals since day one.<\/p>\n

That said, we realized that we were probably not “advertising” the bare-bone, stripped-down aspect of Devise well enough. That’s why I am writing this blog post. Devise already makes it easy for you to customize your own views, using the generator rails g devise:views<\/code> which copies the views to your application. But what if you want to roll out your own views AND controllers?<\/p>\n

To show you how we can achieve that, let’s write some code! The first step is to create a Rails application:<\/p>\n

rails new devise-only-model<\/pre>\n

Next, we will add Devise to the Gemfile:<\/p>\n

gem \"devise\", \"~> 1.4.6\"<\/pre>\n

And run the installation generator:<\/p>\n

bundle install && rails g devise:install<\/pre>\n

The installation generator is going to give you some extra instructions, so don’t forget to do that as well. Next, let’s generate our basic User model, but we will pass an extra parameter called --skip-routes<\/code>:<\/p>\n

rails g devise User --skip-routes<\/pre>\n

By passing this extra parameter, Devise is going to generate everything as usual, with a small difference on config\/routes.rb<\/code>:<\/p>\n

devise_for :users, :skip => :all<\/pre>\n

This parameter tells Devise to not generate any route at all. You can check that by executing bundle exec rake routes<\/code>. However, you may be wondering: why can’t we simply remove the devise_for<\/code> call? If we remove the route, Devise wouldn’t actually know that you have added Devise configuration to the User model, as all models are lazy loaded. So we need the route to tell Devise it needs to setup the appropriate helpers for the user, like authenticate_user!<\/code>.<\/p>\n

With Devise configured, we are ready to create the controllers and views on our own. In this blog post, we are going to create the SessionsController<\/code> as an example allowing us to sign in and sign out. First, let’s add our routes:<\/p>\n

\r\nroot :to => \"sessions#new\"\r\npost \"\/users\/sign_in\"    => \"sessions#create\"\r\ndelete \"\/users\/sign_out\" => \"sessions#destroy\"\r\n<\/pre>\n

Our SessionsController<\/code> at app\/controllers\/sessions_controller<\/code> looks like:<\/p>\n

\r\nclass SessionsController < ApplicationController\r\n  # For security purposes, Devise just authenticates an user\r\n  # from the params hash if we explicitly allow it to. That's\r\n  # why we need to call the before filter below.\r\n  before_filter :allow_params_authentication!, :only => :create\r\n\r\n  def new\r\n    @user = User.new(params[:user])\r\n  end\r\n\r\n  def create\r\n    # Since the authentication happens in the rack layer,\r\n    # we need to tell Devise to call the action \"sessions#new\"\r\n    # in case something goes bad. Feel free to change it.\r\n    user = authenticate_user!(:recall => \"sessions#new\")\r\n    flash[:notice] = \"You are now signed in!\"\r\n    sign_in user\r\n    redirect_to root_path\r\n  end\r\n\r\n  def destroy\r\n    sign_out\r\n    flash[:notice] = \"You are now signed out!\"\r\n    redirect_to root_path\r\n  end\r\nend\r\n<\/pre>\n

The controller implementation is quite straightforward. With the controller in hands, we just need to generate the view for the new action at app\/views\/sessions\/new.html.erb<\/code>:<\/p>\n

\r\n<% if user_signed_in? %>\r\n  You are signed in as <%= current_user.email %>. <%= link_to \"Sign out\", users_sign_out_path, :method => :delete %>.\r\n<% else %>\r\n  <%= form_for @user, :url => users_sign_in_path do |f| %>\r\n  
<%= f.label :email %>
\r\n <%= f.email_field :email %><\/div>\r\n
<%= f.label :password %>
\r\n <%= f.password_field :password %><\/div>\r\n
<%= f.check_box :remember_me %> <%= f.label :remember_me %><\/div>\r\n
<%= f.submit \"Sign in\" %><\/div>\r\n <% end %>\r\n<% end %>\r\n<\/pre>\n

The view shows a message if the user is signed in, otherwise it shows a sign in form. Now we are almost ready to check if it works. First, we need to run the migrations:<\/p>\n

bundle exec rake db:migrate<\/pre>\n

Remove the index page:<\/p>\n

rm public\/index.html<\/pre>\n

And create a user in the database so we can sign in. We can do that in rails console<\/code>:<\/p>\n

\r\nUser.create!(:email => \"jose@example.com\", :password => \"123456\")\r\n<\/pre>\n

Now, start the server and you are ready to sign in and sign out. You can also block user access in any controller by calling authenticate_user!<\/code> in a before filter. Just remember that, if you add the filter to your application controller, remember to skip the filter on the sessions controller, otherwise you won’t be able to sign in in the first place.<\/p>\n

You can now freely proceed to implement the other controllers and views in your application. Keep in mind that if you have devise :recoverable<\/code> in your model, all the related methods like User.send_reset_password_instructions<\/code> will already be available in your model, so you can use them straight away to implement your own reset password feature. Since Devise use all those methods internally, if you have any questions about implementing your own reset password feature, you can always take a look at Devise own controllers<\/a> for some help.<\/p>\n

I hope this post can help you to roll out your own controllers if this is the kind of feature you expect from Devise. Also, if you are worried about the overhead of using Devise even if you are not using its controllers, there is no need to worry at all. Devise does the smart thing and only loads the controllers you are actually using. Also, it lazily loads all behaviors, so if you are not using recoverable, no code related to recoverable will be loaded at all.<\/p>\n

It is important to keep in mind that Devise was built by us to be flexible and capable of handling different requirements from different clients, so it is PlataformaTec’s priority to have it as flexible as possible! So, what would you like to see in “bare-bone, stripped-down Devise” in order to better use it in your applications?<\/p>\n","protected":false},"excerpt":{"rendered":"

Last week I spoke at Silicon Valley Ruby Group about PlataformaTec’s open source tools, mainly Devise, Simple Form and Responders. When talking about Devise, I’ve mentioned that, before creating Devise, we were alternating between using Authlogic or Clearance in our projects. However, we soon realized that we needed a solution that was as customizable as … \u00bb<\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[37,36,7],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2263"}],"collection":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=2263"}],"version-history":[{"count":26,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2263\/revisions"}],"predecessor-version":[{"id":2298,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2263\/revisions\/2298"}],"wp:attachment":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=2263"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=2263"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=2263"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}