$ rails new sample_app --skip-test-unit # not to generate a test directoryinclude gems: RSpec for testing purposes, We also include the Capybara gem, which allows us to simulate a user’s interaction with the sample application using a natural English-like syntax, together with Selenium, one of Capybara’s dependencies. $ vim ./Gemfile gem 'rspec-rails', '2.13.1' group :test do gem 'selenium-webdriver', '2.0.0' gem 'capybara', '2.1.0' end $ bundle install $ bundle update $ bundle install configure Rails to use RSpec in place of Test::Unit .$ rails generate rspec:install Configure gith(hub) integration$ git init
$ git add .
$ git commit -m "Initial commit" create github repository https://github.com/new $ git remote add origin https://github.com/<username>/sample_app.git $ git push -u origin master $ git checkout -b static-pages $ git push --all $ rails generate controller StaticPages home help --no-test-framework # suppress the generation of the default RSpec tests create app/controllers/static_pages_controller.rb route get "static_pages/help" route get "static_pages/home" invoke erb create app/views/static_pages create app/views/static_pages/home.html.erb create app/views/static_pages/help.html.erb invoke helper create app/helpers/static_pages_helper.rb invoke assets invoke coffee create app/assets/javascripts/static_pages.js.coffee invoke scss create app/assets/stylesheets/static_pages.css.scss we have passed the controller name as so-called CamelCase, which leads to the creation of a controller file written in snake case, so that a controller called StaticPages yields a file called static_pages_controller.rb .
using snake case at the command line also works: the command $ rails
generate controller static_pages also generates a controller called static_pages_controller.rb .Because Ruby uses CamelCase for class names,
my preference is to refer to controllers using their CamelCase names,
but this is a matter of taste. (Since Ruby filenames typically use snake
case, the Rails generator converts CamelCase to snake case using the underscore method.) Look into ./config/routes.rb Notice the correspondence between actions and views. (home/help) ==> (get "static_pages/home" maps
requests for the URL /static_pages/home to the home action in the
StaticPages controller. Moreover, by using get we arrange for the route
to respond to a GET request, which is one of the fundamental HTTP verbs
supported by the hypertext transfer protocol (Box 3.3). In our case,
this means that when we generate a home action inside the StaticPages
controller we automatically get a page at the address
/static_pages/home. PATCH
and DELETE, are designed for updating and destroying things on the
remote server. These requests are less common than GET and POST since
browsers are incapable of sending them natively, but some web frameworks
(including Ruby on Rails) have clever ways of making it seem like
browsers are issuing such requests. http://localhost:3000/static_pages/home $ vim ./app/controllers/static_pages_controller.rb class StaticPagesController < ApplicationController def home end def help end end Note
that, unlike the demo Users and Microposts controllers, the StaticPages
controller does not use the standard REST actions. This is normal for a
collection of static pages—the REST architecture isn’t the best
solution to every problem. when visiting the URL /static_pages/home, Rails looks in the StaticPages controller and executes the code in the home action, and then renders the view. the home action here is empty, so all visiting /static_pages/home does is render the view. home.html.erb/ help.html.erb )Apply Test Cases$ rails generate integration_test static_pages invoke rspec $ vim ./spec/requests/static_pages_spec.rb create spec/requests/static_pages_spec.rb require 'spec_helper' describe "StaticPages" do describe "Home page" do # The first line indicates that we are describing the Home page. # This description is just a string and is not important. it "should have the content 'Sample App'" do # describing string visit '/static_pages/home' # simulate visiting the URL /static_pages/home in a browser expect(page).to have_content('Sample App') # when you visit the Home page at /static_pages/home, the content should contain the words “Sample App” end end end # What goes inside the quote marks is irrelevant to RSpec, and is intended to be descriptive to human readers. $ vim ./spec/spec_helper.rb # This file is copied to spec/ when you run 'rails generate rspec:install' . . . RSpec.configure do |config| . . . config.include Capybara::DSL end $ vim app/views/static_pages/home.html.erb <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> About Page Test Driven Developmentfirst create the testcase $ vim ./spec/requests/static_pages_spec.rb describe "About page" do it "should have the content 'About Us'" do visit '/static_pages/about' expect(page).to have_content('About Us') end end $ bundle exec rspec spec/requests/static_pages_spec.rb # see the test failing Error is No route matches [GET] "/static_pages/about" This is a hint that we need to add /static_pages/about to the routes file, $ vim config/routes.rb get "static_pages/about" $ bundle exec rspec spec/requests/static_pages_spec.rb Error is The action 'about' could not be found for StaticPagesController $ vim app/controllers/static_pages_controller.rb def about # Add about action end $ bundle exec rspec spec/requests/static_pages_spec.rb $ vim <h1>About Us</h1> <p> The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p> $ bundle exec rspec spec/requests/static_pages_spec.rb Test passed Add dynamic content to static pagesCurrently the title of all the pages are SampleApp, we want each page to have its own title. $ git mv ./app/views/layouts/application.html.erb foobar # disable layout $ vim ./spec/requests/static_pages_spec.rb it "should have the right title" do visit '/static_pages/home' expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home") end $ bundle exec rspec spec/requests/static_pages_spec.rb #fail $ vim ./app/views/static_pages/home.html.erb <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | Home</title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html> $ bundle exec rspec spec/requests/static_pages_spec.rb # pass EmbeddedRuby for Dynamic title <% provide(:title, 'Help') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <body> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="http://railstutorial.org/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="http://railstutorial.org/book">Rails Tutorial book</a>. </p> </body> </html> provide function and associate the string ’Home’ with the label :title. The practical differences between provide and content_for are well explained here: http://api.rubyonrails.org/classes/ActionController/Streaming.html#label-Communication+between+layout+and+templateUsing Layouts (to abstract) (eliminate duplication)Restore the layout page that we discarded $ git mv foobar app/views/layouts/application.html.erb <%= yield %> This code is responsible for inserting the contents of each page into the layout. using this layout ensures that, for example, visiting the page /static_pages/home converts the contents of home.html.erb to HTML and then inserts it in place of <%= yield %>.When streaming, rendering happens top-down instead of inside-out. YIELD: wait for it to provide it later. (to be filled) Conclusion:Seen from the outside, this chapter hardly accomplished anything: we started with static pages, and ended with… mostly static pages. But appearances are deceiving: by developing in terms of Rails controllers, actions, and views, we are now in a position to add arbitrary amounts of dynamic content to our site. Seeing exactly how this plays out is the task for the rest of this tutorial. In the layout: <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> This relies on the definition of a page title (using provide) in each view, <% provide(:title, 'Home') %> But what if we don’t provide a title? It’s a good convention to have a base title we use on every page, with an optional page title if we want to be more specific. $ vim ./app/helpers/application_helper.rb # created when sample_app initialized. module ApplicationHelper # Returns the full title on a per-page basis. def full_title(page_title) base_title = "Ruby on Rails Tutorial Sample App" if page_title.empty? base_title else "#{base_title} | #{page_title}" end end end $ vim ./app/views/layouts/application.html.erb <title><%= full_title(yield(:title)) %></title> Now remove redundant Home page title, first modify the test $ vim ./spec/requests/static-pages_spec.rb expect(page).to have_title("Ruby on Rails Tutorial Sample App") $ bundle exec rspec spec/requests/static_pages_spec.rb # test fails $ vim ./app/views/static_pages/home.html.erb # delete the provide title line $ bundle exec rspec spec/requests/static_pages_spec.rb # test pass check it out $ rails c # OR $ rails console works on top of ruby interactive console $ irb Types of environment for rails: -Development (default) -Test -Production Strings >> "foo" + "bar" # String concatenation => "foobar" >> first_name = "Michael" # Variable assignment => "Michael" >> "#{first_name} Hartl" # String interpolation => "Michael Hartl" >> puts "foo" # put string foo => nil The puts method operates as a side-effect: the expression puts "foo" prints the string to the screen and then returns literally nothing: nil is a special Ruby value for “nothing at all”. >> print "foo" # print string (same as puts, but without the newline) foo=> nil Ruby won’t interpolate into single-quoted strings: "".empty? true "foobar".length puts "Both strings are empty" if x.empty? && y.empty?
>> string = "foobar" >> puts "The string '#{string}' is nonempty." unless string.empty? The string 'foobar' is nonempty. => nil >> def string_message(string) >> if string.empty? >> "It's an empty string!" >> else >> "The string is nonempty." >> end >> endmodules give us a way to package together related methods, which can then be mixed in to Ruby classes using include "abcdxkjdcxw".split("x") array a = [42, 8, 17] x = a.length a.sort .shuffle .reverse a.sort! Blocks
>> a[2..(a.length-1)] # Explicitly use the array's length. => [2, 3, 4, 5, 6, 7, 8, 9] 3.times { puts "Betelgeuse!" } # 3.times takes a block with no variables. (1..5).map { |i| i**2 } # The ** notation is for 'power'. %w[a b c].map {|temp| temp.upcase} # downcase => ["A", "B", "C"] ('a'..'z').to_a.shuffle[0..7].join 0..7 take range Hashesare essentially arrays that aren’t limited to integer indices.> user = {} > user["firstname"] = "Ali" => "Ali" > user => {"firstname"=>"Ali"} It’s important to note that the curly braces for hashes have nothing to do with the curly braces for blocks. So far we’ve used strings as hash keys, but in Rails it is much more common to use symbols instead. Symbols look kind of like strings, but prefixed with a colon instead of surrounded by quotes. For example, :name is a symbol. You can think of symbols as basically strings without all the extra baggage user = { :name => "Michael Hartl", :email => "michael@example.com" } Since it’s so common for hashes to use symbols as keys, Ruby 1.9 supports a new syntax just for this special case: >> h1 = { :name => "Michael Hartl", :email => "michael@example.com" } => {:name=>"Michael Hartl", :email=>"michael@example.com"} >> h2 = { name: "Michael Hartl", email: "michael@example.com" } => {:name=>"Michael Hartl", :email=>"michael@example.com"} >> h1 == h2 => true Nested hashes.>> params = {} # Define a hash called 'params' (short for 'parameters'). => {} >> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" } => {:name=>"Michael Hartl", :email=>"mhartl@example.com"} >> params => {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}} >> params[:user][:email] => "mhartl@example.com" inspect methodreturns a string with a literal representation of the object it’s called on: puts (1..5).to_a.inspect # Put a literal array. [1, 2, 3, 4, 5]
using inspect to print an object is common enough that there’s a shortcut for it, the p function:
>> p :name # Same as 'puts :name.inspect' :name Note:
CSS<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> stylesheet_link_tag function with two
arguments: a string, indicating the path to the stylesheet, and a hash
with two elements, indicating the media type and telling Rails to use
the turbolinks feature (new in Rails 4). This will create the output:<link data-turbolinks-track="true" href="/assets/application.css" media="all" rel="stylesheet" /> ClassesEverything is a class: literal constructor vs named constructor String -> Object -> BasicObject -> nil self = this in java.
Rails adds a blank? method to Ruby. >> "".blank? => true >> " ".empty? => false >> " ".blank? => true >> nil.blank? => true nil is blank; since nil isn’t a string, this is a hint that Rails actually adds blank? to String ’s base class, which (as we saw at the beginning of this section) is Object itself.instance variables is that they are automatically available in the views, Instance variables always begin with an @ sign, and are nil when undefined.$ vim example_user.rb class User
attr_accessor :name, :email # creates “getter” and “setter” methods
def initialize(attributes = {}) # called when we execute $ irb >> require './example_user.rb' >> u = user.new >> u.name = 'Ali' >> u.format =================================================================================================== =================================================================================================== We’ll make use of Bootstrap, an open-source web design framework from Twitter. $ vim app/views/layouts/application.html.erb The added lines are in blue: <!DOCTYPE html> Rails 4 uses HTML5 by default (indicated by <!DOCTYPE html>); we include some JavaScript code (known as an “HTML5 shim”) to work around the issue, this only works for IE: The first comment it’s actually a conditional comment supported by Internet Explorer browsers for just this sort of situation. (if IE is less than v9 ...) $ vim app/views/static_pages/home.html.erb <div class="center hero-unit"> Download and place rails.png logo to app/assets/images/ Check it out: http://localhost:3000/static_pages/home Bootstrap, a framework from Twitter that makes it easy to add nice web design and user interface elements to an HTML5 application. The Bootstrap framework natively uses the LESS CSS language for making dynamic stylesheets. Add Bootstrap by adding the bootstrap-sass gem to the Gemfile . Instead of LESS, Rails asset pipeline supports the (very similar) Sass language by default. bootstrap-sass converts LESS to Sass and makes all the necessary Bootstrap files available to the current application. $ vim ./Gemfile source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0 gem 'rails', '4.0.0' gem 'bootstrap-sass', '2.3.2.0' . . . $ gem install bundler # bundler needed once $ bundle install $ vim config/application.rb Add config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif) as the last line inside class Application < Rails::Application $ vim app/assets/stylesheets/custom.css.scss @import "bootstrap"; This one line includes the entire Bootstrap CSS framework Any stylesheets in this directory will automatically be included as part of the application.css file included in the site layout.Filename custom.css.scss includes the .css extension, which indicates a CSS file, and the .scss extension, which indicates a “Sassy CSS” file and arranges for the asset pipeline to process the file using Sass. With and without importing Bootstrap Copyright and license Ruby on Rails Tutorial: Learn Web Development with Rails. Copyright © 2013 by Michael Hartl. All source code in the Ruby on Rails Tutorial is available jointly under the MIT License and the Beerware License. |