Notes on Rails
This works along with Simply Rails: http://my.safaribooksonline.com/9780980455205
To start developing, go to terminal > documents/rails/rallywire then type ruby script/server, then you can visit it at http://0.0.0.0:3000
To play around with your application in the console, terminal to the directory and type ruby script/console
1. Get to the irb
⁃ go to terminal and type irb
⁃ This lets you play with ruby
2. Create the rails app
⁃ In terminal, go to the folder in which you want you application to be created (e.g., cd MattDyor/Documents/Rails is where I put all of my rails apps), and type rails APPLICATION_NAME
⁃ rails RallyWire
3. Create the model
⁃ open terminal
⁃ terminal browse to the directory of the application (create it in finder if you need to)
⁃ terminal rails script/generate model RallyPoint name:string
⁃ fixed by running rails RallyWire – confirm and removei needed to copy the files from the archive over to get this to work
⁃ generating the model automatically adds an id, created_at, and updated_at
⁃ terminal rake db:migrate
4. Naming conventions:
⁃ Use nouns as your class nouns (eg-use Subscription instead of Subscribe)
⁃ This means you are describing what you are storing, not what you are doing
⁃ Camelcase the class (Subscription, RallyPoint)
⁃ lowercase and pluralize the table (subscriptions, rally_points)
⁃ most references other than the class are lowercase (eg-the resources in the config)
⁃ instance variables can be small, but should be lower
⁃ attributes of the class are lower case
5. Now add data to the model
⁃ terminal ruby script/console
⁃ rp = RallyPoint.new
⁃ rp.name = ‘Clubhouse Rally’
⁃ rp.save
6. Now create the controller
⁃ ruby script/generate controller RallyPoints index (note the plural here and not above)
⁃ this will create our index page only
⁃ the controller that is created is rally_point
⁃ update the index method in the controller
⁃ def index
⁃ @rally_point = RallyPoint.find_by_name(‘Clubhouse Rally’)
⁃ end
7. Now update the view file (index.html.erb)
⁃ <p><%= @rally_point.name%></p>
8. Firing up the server
⁃ ruby script/server
⁃ you should be able to visit at 0.0.0.0:3000
9. Add a form to create new entries
⁃ In textmate, go to app>views>XXX(datatype), right click and select new
⁃ Call the file new.html.erb
⁃ <% form_for @rally_point do |f|%>
⁃ <p>
⁃ name: <br />
⁃ <%= f.text_field :name%>
⁃ </p>
⁃ <p>
⁃ <%= submit_tag %>
⁃ </p>
⁃ <% end %>
⁃ update the controller–add a new method
⁃ @rally_point = RallyPoint.new
10. Now add the helper URLs to map URLs to functions
⁃ go to config/routes.rb
⁃ add map.resources :rally_points
⁃ Now you should be able to visit 0.0.0.0:3000/rally_points/new
11. A brief review
⁃ The route directory is /rally_points (plural)
⁃ In the config/routes.rb, there is the update above and the following
⁃ map.root :controller => “rally_points”
⁃ the class is RallyPoint, everything else is rally_point
12. Create your create method in the controller
⁃ def create
⁃ @rally_point = RallyPoint.new(params[:rally_point])
⁃ @rally_point.save
⁃ redirect_to rally_points_path
⁃ end
13. Test your controller
⁃ navigate to /rally_point/new
⁃ create a record
⁃ fire up an irb console (terminal > cd to project directory, ruby script/console)
⁃ Story.find(2)
⁃ Story.find(:all)
⁃ Story.find(:all).last
⁃ Story.find(:first,
rder => ‘id DESC’)
⁃ Story.find_by_name(‘Hello World Today’)
⁃ Story.find(:first,
rder => ‘RANDOM()’)
14. Create your layout
⁃ create, in ap>views>layouts, a layout named application.html.erb
⁃ standard XHTML web page with two extra fields: stylesheet under the title and yield in the body
⁃ <%= stylesheet_link_tag ‘style’ %>
⁃ this will point the style sheet to /public/stylesheets/style.css
⁃ <div id=”content”>
⁃ <%= yield %>
⁃ </div>
15. Create your style sheet
⁃ body
⁃ {
⁃ background-color: #666;
⁃ margin: 15px 25px;
⁃ font-family: Helvetica, Arial, sans-serif;
⁃ }
⁃ #content
⁃ {
⁃ background-color: #fff;
⁃ border: 10px solid #ccc;
⁃ }
16. Add some flash
⁃ In the style.css
⁃ #notification {
⁃ border: 5px solid #9c9;
⁃ background-color: #cfc;
⁃ padding: 5px;
⁃ margin:; 10px 0;
⁃ }
⁃ in the controller, add this line after the .save command
⁃ flash[:notice] = ‘Rally Point submission succeeded.’
⁃ add this to your index.html.erb
⁃ <% unless flash[:notice].blank? %>
⁃ <div id=”notification”><%= flash[:notice] %></div>
⁃ <% end %>
17. Validation
⁃ add this to the model (app/models/rally_point.rb) inside the class
⁃ validates_presence_of :name
⁃ now update the controller>create method so that it does not redirect if there is a (validation) problem
⁃ if @rally_point.save
⁃ flash[:notice] = ‘Rally Point submission succeeded.’
⁃ redirect_to rally_points_path
⁃ else
⁃ render :action => ‘new’
⁃ end
⁃ add this to new.html.erb
⁃ <%= error_messages_for ‘rally_point’ %>
18. Now create unit tests
⁃ unit tests are defined in test>unit>rally_point_test.rb
⁃ def test_should_not_be_valid_without_name
⁃ rp = RallyPoint.create(:name=>nil)
⁃ assert rp.errors.on(:name)
⁃ end
⁃ def test_should_create_story
⁃ rp=RallyPoint.create(:name=>’Big Juicy Unit Test’)
⁃ assert rp.valid?
⁃ end
⁃ run your unit tests in terminal with rake test:units
19. Now create functional tests
⁃ functional tests are defined in test>functional?rally_points_controller_test.rb
⁃ def test_should_show_index
⁃ get :index
⁃ assert_response :success
⁃ assert_template ‘index’
⁃ assert_not_nil assigns(:rally_point)
⁃ end
⁃ run your functional tests with rake test:functionals
⁃ assert_select ‘form p’, :count => 3
⁃ Test the create method:
⁃ def test_should_add_rally_point
⁃ post :create, :rally_point => {
⁃ :name => ‘test from test’
⁃ }
⁃ #new_record? tests whether you have a new, unsaved record
⁃ assert ! assigns(:rally_point).new_record?
⁃ #i am not sure whether this actually saves it to the database…it does not appear to have
⁃ assert_redirected_to rally_points_path
⁃ assert_not_nil flash[:notice]
⁃ end
⁃ you can run all the tests with rake test
20. Create the user security bits
⁃ ruby script/generate model User login:string password:string name:string email:string phone:string
⁃ go to the created model (db/migrate/XXXcreate user.rb) and add this line outside of the create_table and inside self.up:
⁃ add_column :rally_points, :user_id, :integer
⁃ add this to the self.down after drop_table
⁃ remove_column :stories, :user_id
⁃ rake db:migrate
⁃ in the user model, add has_many :rally_points
⁃ in the rally_point model, add belongs_to :user
⁃ Create the sessions controller for managing login (note: this is sessions…with an s, not sure why)
⁃ ruby script/generate controller Sessions new create destroy
⁃ including new create and destroy adds the methods and creates the templates
⁃ update routes.rb with this (note the singular–this is a singleton object, meaning that each user can have only one session)
⁃ map.resource :session
⁃ since this is singular, all of the urls will be singular (eg., session/new)
⁃ update the new.html.erb file
⁃ <% form_tag session_path do %>
⁃ <p>Please log in.</p>
⁃ <p>
⁃ <label>Username: </label>
⁃ <%= text_field_tag ‘ login’ %>
⁃ </p>
⁃ <p>
⁃ <label>Password: </label>
⁃ <%= password_field_tag ‘ password’ %>
⁃ </p>
⁃ <p><%= submit_tag ‘ login’ %></p>
⁃ <% end %>
⁃ update sessions_controller.rb (in app>controllers)
⁃ def create
⁃ @current_user = User.find_by_login_and_password(params[:login], params[:password])
⁃ if @current_user
⁃ session[:user_id] = @current_user.id
⁃ redirect_to rally_points_path
⁃ else
⁃ flash[:notice]=”Incorrect username or password. Please try again.”
⁃ render :action=>’new’
⁃ end
⁃ end
⁃ current_user = User. find session[:user_id]
21. Apply a login filter
⁃ Add this code to the application_controller (app/controller/)
⁃ before_filter :fetch_logged_in_user
⁃ protected
⁃ def fetch_logged_in_user
⁃ return unless session[:user_id]
⁃ @current_user = User.find_by_id(session[:user_id])
⁃ end
⁃ Add this code to the application layout (app/views/layout)
⁃ <div id=”login_logout”>
⁃ <% if @current_user %>
⁃ Logged in as:
⁃ <%= @current_user.login %>
⁃ <em><%= link_to “(Logout) “, session_path, :method => :delete %></em>
⁃ <% else %>
⁃ <em>Not logged in.</em>
⁃ <%= link_to ‘Login’, new_session_path %>
⁃ <% end %>
⁃ </div>
⁃ Add the formatting to the style sheet (public/stylesheets)
⁃ #login_logout {
⁃ float: right;
⁃ color: #999;
⁃ font-size: smaller;
⁃ }
⁃ Add this to the session_controller (app/controllers)
⁃ def destroy
⁃ session[:user_id] = @current_user = nil
⁃ end
⁃ Update your logout page
⁃ <h2>Logout successful</h2>
⁃ <%= link_to ‘Back to the home page’ , rally_points_path %>
⁃ Add Navigation to the template (within the body)
⁃ <ul id=”navigation”>
⁃ <li><%= link_to ‘Front page rally points’, rally_points_path %></li>
⁃ <li><%= link_to ‘Submit a new rally point!’, new_rally_point_path %></li>
⁃ </ul>
⁃ Helper methods for requiring login before visiting pages by updating the applicationcontroller (below the protected)
⁃ def logged_in?
⁃ ! @current_user. nil?
⁃ end
⁃ helper_method :logged_in?
⁃ NOTE: the second helper method is so that you can access this method in the views (first method is for access via controller)
⁃ And then add this that actually requires the user to login
⁃ def login_required
⁃ return true if logged_in?
⁃ session[:return_to] = request.request_uri
⁃ redirect_to new_session_path and return false
⁃ end
⁃ Update the rally_points_controller right under the class Intro
⁃ before_filter :login_required,
nly => [ :new, :create ]
⁃ Replace the redirection line on sessions_controller with this:
⁃ session[:user_id] = @current_user.id
⁃ if session[:return_to]
⁃ redirect_to session[:return_to]
⁃ session[:return_to] = nil
⁃ else
⁃ redirect_to rally_points_path
⁃ end
⁃ Associate a user with a story by replacing the top line of the create method of the rally_points_controller with the following line
⁃ @rally_point = @current_user.rally_points. build params[:rally_point]
⁃ And show the user along with their story
⁃ <%= @rally_point.user.login %>
⁃ You may need to delete all your rally points or update them so that they have a
23. Code snippets
⁃ <%= link_to @rally_point.name, @rally_point.link %>
⁃ <%= link_to “New”, new_rally_point_path %>
⁃ url helpers are rally_points_path [/rally_points], rally_point_path(@rally_point) [/rally_points/1], edit_rally_point(@rally_point) [/rally_points/1/edit)
