12: User Management




Learning Rails show

Summary: <h2>Goals</h2> <p>In this lesson, we work towards implementing an administrative dashboard by expanding the user controller generated by restful_authentication to give us full abilities to manage user data.</p> <h2>Setup</h2> <p>We begin with the code with which we ended Lesson 11. These zip files contain the beginning and ending states of the code:</p> <ul> <li><a href="/learningrails_11.zip">Learning Rails example app code as of the end of Lesson 11</a></li> <li><a href="/learningrails_12.zip">Learning Rails example app code as of the end of Lesson 12</a></li> </ul> <h2>Clean up session create with a notice if user incorrectly enters credentials</h2> <p>Before diving in to the core work in this lesson, let’s continue to do some small changes to improve the experience our application provides. One pain point occurs when you try to log in and type the wrong credentials.</p> <p>Let’s put a notice in the create failure path, before re-rendering the edit dialog</p> <pre> flash[:notice] = "Try entering your credentials again" </pre> <h2>Create a default admin user (migration)</h2> <p>Let’s use a migration called a “data migration”. It doesn’t change the structure of the database. Instead it loads data, in this case, a default user account.</p> <pre> script/generate migration AddDefaultUser </pre> <pre> self.up if !User.find_by_login('admin') User.create(:login =&gt; 'admin', :email =&gt; 'admin@sample.com', :password =&gt; 'changeme', :password_confirmation =&gt; 'changeme') end end </pre> <p>We aren’t creating a down migration in this case. We don’t know if we were the ones who created this account for sure, so we won’t delete it when this migration is run in reverse.</p> <p>Run the migration in the terminal:</p> <pre> rake db:migrate </pre> <h2>Add a user list page</h2> <p>The “restful_authentication” plugin’s generate creates a RESTful controller, but it only creates the new and create actions. We need to fill the rest out and can use the Pages controller as a model.</p> <p>Add an index action to users controller.</p> <pre> def index @users = User.find(:all) end </pre> <p>Create users/index view in the users directory (index.html.erb). Also prepare by adding links to show, edit, and delete. Put a link at the bottom to create new users.</p> <pre> &lt;h1&gt;Listing Users&lt;/h1&gt; &lt;table&gt; &lt;tr&gt; &lt;th&gt;Login&lt;/th&gt; &lt;th&gt;Email&lt;/th&gt; &lt;/tr&gt; &lt;% for user in @users %&gt; &lt;tr&gt; &lt;td&gt;&lt;%=h user.login %&gt;&lt;/td&gt; &lt;td&gt;&lt;%=h user.email %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Show', user %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Edit', edit_user_path(user) %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Delete', user, :confirm =&gt; "Are you sure you want to delete '#{user.login}'?", :method =&gt; :delete %&gt;&lt;/td&gt; &lt;/tr&gt; &lt;% end %&gt; &lt;/table&gt; &lt;br /&gt; &lt;%= link_to 'New User', new_user_path %&gt; </pre> <p>Take a look…seems to work, but we need to fill out the rest of the actions and the appropriate views.</p> <h2>Add show user page</h2> <p>Add a show action to the users controller.</p> <pre> def show @user = User.find(params[:id]) end </pre> <p>Add the show view (show.html.erb).</p> <pre> &lt;p&gt; &lt;b&gt;Login:&lt;/b&gt; &lt;%=h @user.login %&gt; &lt;/p&gt; &lt;p&gt; &lt;b&gt;Email:&lt;/b&gt; &lt;%=h @user.email %&gt; &lt;/p&gt; &lt;p&gt; &lt;b&gt;Password:&lt;/b&gt; [secret] &lt;/p&gt; &lt;%= link_to 'Edit', edit_user_path(@user) %&gt; | &lt;%= link_to 'Back', users_path %&gt; </pre> <h2>Add a user delete function</h2> <p>Add destroy action to users controller.</p> <pre> def destroy @user = User.find(params[:id]) @user.destroy redirect_to(users_url) end </pre> <p>Note the link is already in the index view and uses a javascript alert dialog to confirm the deletion with the user.</p> <h2>Add a edit/Update user functions</h2> <p>Add edit and update actions to the users controller. New/create and edit/update always work in pairs in the RESTful implementation in Rails 2.</p> <pre> def edit @user = User.find(params[:id]) end def update @user = User.find(params[:id]) if @user.update_attributes(params[:user]) flash[:notice] = 'User was successfully updated.' redirect_to(user_path(@user)) else render :action =&gt; 'edit' end end </pre> <p>Create a corresponding view for edit. It is nearly identical to the new view, but we changed the form_for line to take advantage of Rails behavior. By specifying the object on the form_for line, Rails automatically generates the proper <span class="caps">URL</span> to our update action and submits the form using the “<span class="caps">PUT</span>” <span class="caps">HTTP</span> method.</p> <pre> &lt;%= error_messages_for :user %&gt; &lt;% form_for @user do |f| -%&gt; &lt;p&gt;&lt;label for="login"&gt;Login&lt;/label&gt;&lt;br/&gt; &lt;%= f.text_field :login %&gt;&lt;/p&gt; &lt;p&gt;&lt;label for="email"&gt;Email&lt;/label&gt;&lt;br/&gt; &lt;%= f.text_field :email %&gt;&lt;/p&gt; &lt;p&gt;&lt;label for="password"&gt;Password&lt;/label&gt;&lt;br/&gt; &lt;%= f.password_field :password %&gt;&lt;/p&gt; &lt;p&gt;&lt;label for="password_confirmation"&gt;Confirm Password&lt;/label&gt;&lt;br/&gt; &lt;%= f.password_field :password_confirmation %&gt;&lt;/p&gt; &lt;p&gt;&lt;%= submit_tag 'Update' %&gt;&lt;/p&gt; &lt;% end %&gt; </pre> <p>Now test all of the pages together. They work!</p> <h2>Wrapping up</h2> <p>Of course, it is tiresome to keep typing the URLs manually to get to these pages. You can navigate directly to the users controller at localhost:3000/users, but it would be a lot easier to have a dashboard that collects all of these links in one easy to access place. Even better, the dashboard should be available from our navigation tags when appropriately logged in.</p> <h2>Coming up</h2> <p>In our next lesson, we’ll add support for “administrative” pages in our simple <span class="caps">CMS</span>, so we can create the dashboard using our own technology. We’ll also tweak the tab navigation so it automatically discovers and displays links without us needing to edit code.</p>