over 2 years ago

備註1:render跟redirect_to的差別:參考:Action Controller - 控制 HTTP 流程
備註2:_error_messages.html.erb
備註3:count,any?,empty?
備註4:pluralizeinclude ActionView::Helpers::TextHelper 才能用
備註5:flash[:success] = "Welcome to the Sample App!"
備註6:

<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

備註7:alert-<%= message_type %>可以變成alert-success,Bootstrap CSS:success, info, warning, and danger。
備註8:只要在production.rb加上一行 config.force_ssl = true,就可以開啟SSL的功能了
備註9:puma gem

  1. 內建的method:debug <%= debug(params) if Rails.env.development? %>
  2. Rails environments: test, development(default的環境), production
    $ rails console
    Loading development environment
    >> Rails.env
    => "development"
    >> Rails.env.development?
    => true
    >> Rails.env.test?
    => false
    
    $ rails console test
    Loading test environment
    >> Rails.env
    => "test"
    >> Rails.env.test?
    => true
    
    server也可以改成production模式,不過會沒有production database
    $ rails server --environment production
    
    這樣才會有production database
      $ bundle exec rake db:migrate RAILS_ENV=production
    
    heroku的預設環境是production
      $ heroku run console
    >> Rails.env
    => "production"
    >> Rails.env.production?
    => true
    
  3. Listing 7.2,@import ,@mixin,@include
  4. 把用console做出來的東西清乾淨:$ bundle exec rake db:migrate:reset,記得要重新啟動server
  5. byebug gem: bundle install之後,在要檢查的controller的action加上debugger
    app/controllers/users_controller.rb
    class UsersController < ApplicationController
    def show
        @user = User.find(params[:id])
        debugger
    end
    def new
    end
    end
    
    byebug prompt: 可以輸入指令看有什麼東西,press Ctrl-D可以離開, 然後記得把debugger刪掉。
    (byebug) @user.name
    "Example User"
    (byebug) @user.email
    "example@railstutorial.org"
    (byebug) params[:id]
    "1"
    
  6. helper裡面定義的method是可以讓所有的view使用的,但是為了方便起見,還是把gravatar_for method放在跟Users Controller相關的helper檔案裡面 By default, methods defined in any helper file are automatically available in any view, but for convenience we’ll put the gravatar_for method in the file for helpers associated with the Users controller.
    app/helpers/users_helper.rb
     module UsersHelper
    # Returns the Gravatar for the given user.
    
    def gravatar_for(user)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
    end
    end
    
  7. form_for 是個helper method,主要是用在model(Active Record)裡的object,所以要先在controller裡面定義好@user。 We can accomplish this in Rails with the form_for helper method, which takes in an Active Record object and constructs a form using the object’s attributes. our first step is to create the User object required as an argument to form_for. The resulting @user variable definition appears in Listing 7.12.
  8. f 是被用來建構HTML表格的,主要是object(@user)的attributes(name, email, password, confirmation) we do need to know is what the f object does: when called with a method corresponding to an HTML form element—such as a text field, radio button, or password field—f returns code for that element specifically designed to set an attribute of the @user object. In other words,creates the HTML needed to make a labeled text field element appropriate for setting the name attribute of a User model.
  9. <%= f.email_field :email %> <%= f.password_field :password %> 會有特殊的格式出現,email會有@,密碼會出現星號。 password fields (type="password") obscure the input for security purposes, as seen in Figure 7.13. (The benefit of using an email field is that some systems treat it differently from a text field; for example, the code type="email" will cause some mobile devices to display a special keyboard optimized for entering email addresses.)
  10. Rails為每個input都弄了一個特別的name attribute (name="user[password]"), []裡面的東西(如password,其實是key,還有對應的value(用戶輸入的值))都會一起被丟到{}裡面去。 As we’ll see in Section 7.4, the key to creating a user is the special name attribute in each input:These name values allow Rails to construct an initialization hash (via the params variable) for creating users using the values entered by the user, as we’ll see in Section 7.3.
    <input id="user_name" name="user[name]" - - - />
    <input id="user_password" name="user[password]" - - - />
    
  11. form_for會為@user弄一個form tag出來,每個object都知道自己的class,然後也會知道這是一個新的object(因為在controller裡面會寫好@user = User.new),所以會自動在form內加上一個post method,post method是專門用來創建新物件的。 The second important element is the form tag itself. Rails creates the form tag using the @user object: because every Ruby object knows its own class (Section 4.4.1), Rails figures out that @user is of class User; moreover, since @user is a new user, Rails knows to construct a form with the post method, which is the proper verb for creating a new object (Box 3.2):<form action="/users" class="new_user" id="new_user" method="post">Here the class and id attributes are largely irrelevant; what’s important is action="/users" and method="post". Together, these constitute instructions to issue an HTTP POST request to the /users URL. We’ll see in the next two sections what effects this has.
  12. resources :users就可以確保路徑符合RESTful URLs規格。 Recall from Section 7.1.2 that adding resources :users to the routes.rb file (Listing 7.3) automatically ensures that our Rails application responds to the RESTful URLs from Table 7.1.In particular, it ensures that a POST request to /users is handled by the create action. Our strategy for the create action is to use the form submission to make a new user object using User.new, try (and fail) to save that user, and then render the signup page for possible resubmission.
  13. render 跟 redirect_to 的不同,參考:Action Controller - 控制 HTTP 流程 This listing includes a second use of the render method, which we first saw in the context of partials (Section 5.1.3); as you can see, render works in controller actions as well.
  14. render 'new' 只會展現new action的template,不會執行裡面的程式。參考:Action Controller - 控制 HTTP 流程
  15. 不同Controller的Template再加上Controller名稱,例如render "events/index"。參考:Action Controller - 控制 HTTP 流程
  16. "user[email]" [email]是user的一個attribute。
    <input id="user_email" name="user[email]" type="email" />
    
    with name "user[email]" is precisely the email attribute of the user hash.
  17. 直接完全接收用戶傳過來的值(params[:user])是非常危險的,尤其是用戶可以把自己設成 admin(admin=’1’)。 The reason is that initializing the entire params hash is extremely dangerous—it arranges to pass to User.new all data submitted by a user. In particular, suppose that, in addition to the current attributes, the User model included an admin attribute used to identify administrative users of the site. (We will implement just such an attribute in Section 9.4.1.) The way to set such an attribute to true is to pass the value admin=’1’ as part of params[:user], a task that is easy to accomplish using a command-line HTTP client such as curl. The result would be that, by passing in the entire params hash to User.new, we would allow any user of the site to gain administrative access by including admin=’1’ in the web request.
    @user = User.new(params[:user])    # Not the final implementation!
    文章裡面直接傳,就出現錯誤訊息了
    
  18. 避免的方法:舊Rails 是用attr_accessible Previous versions of Rails: used a method called attr_accessible in the model layer to solve this problem
  19. Rails 4.0:strong parameters指定哪些變數可以傳進來。 Rails 4.0: the preferred technique is to use so-called strong parameters in the controller layer
  20. params必須要有:user這個屬性,而我們允許:user的屬性包括name, email, password, 和 password confirmation。 In the present instance, we want to require the params hash to have a :user attribute, and we want to permit the name, email, password, and password confirmation attributes (but no others). We can accomplish this as follows:
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
    
  21. 因為 user_params只會在Users controller 內部存取,不能備外部用戶透過網路存取,所以要設成private。 Since user_params will only be used internally by the Users controller and need not be exposed to external users via the web, we’ll make it private using Ruby’s private keyword, as shown in Listing 7.17. (We’ll discuss private in more detail in Section 8.4.) @user = User.new(params[:user]) # Not the final implementation!會變成@user = User.new(user_params)
    app/controllers/users_controller.rb
    class UsersController < ApplicationController
    def create
    @user = User.new(user_params)
    if @user.save
        # Handle a successful save.
    
    else
        render 'new'
    end
    end
    private
    def user_params
        params.require(:user).permit(:name, :email, :password,:password_confirmation)
    end
    end
    
  22. form-control:Bootstrap的格式 。
  23. errors.full_messages可以看到錯誤訊息。
  24. render an error-messages partial
    app/views/users/new.html.erb
    <%= render 'shared/error_messages' %>
    
    render跟redirect_to的差別:參考:Action Controller - 控制 HTTP 流程 render 其他controllerc/ action資料夾 /檔案
  25. shared/error_messages:convention:share資料夾(要自己mkdir)內放置的partial,可以被用在橫跨多個controller的view裡面。 This reflects the common Rails convention of using a dedicated shared/ directory for partials expected to be used in views across multiple controllers.
    app/views/users/new.html.erb
    <% provide(:title, 'Sign up') %>
    <h1>Sign up</h1>
    <div class="row">
    <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>
      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>
      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>
      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>
      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
    </div>
    </div>
    
  26. 作一個error_messages.html.erb partial We then need to create the error_messages.html.erb partial file using our text editor as usual. The contents of the partial appear in Listing 7.19.
    $ mkdir app/views/shared
    
    app/views/shared/_error_messages.html.erb
     <% if @user.errors.any? %>
    <div id="errorexplanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.fullmessages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
    </div>
    <% end %>
    
  27. empty?,any?,兩者剛好相反 empty? returning true for an empty object and false otherwise. The any? method returning true if there are any elements present and false otherwise. any? is just the opposite of empty?.
    >> user.errors.empty?
    => false
    >> user.errors.any?
    => true
    
  28. error_message的CSS:#error_explanation。出現錯誤訊息時,會自動產生:.field_with_errors Note that Listing 7.19 includes the CSS id error_explanation for use in styling the error messages. (Recall from Section 5.1.2 that CSS uses the pound sign # to style ids.) In addition, after an invalid submission Rails automatically wraps the fields with errors in divs with the CSS class field_with_errors. These labels then allow us to style the error messages with the SCSS shown in Listing 7.20, which makes use of Sass’s @extend function to include the functionality of the Bootstrap class has-error.
    app/assets/stylesheets/custom.css.scss
    #error_explanation {
    color: red;
    ul {
    color: red;
    margin: 0 0 30px 0;
    }
    }
    .field_with_errors {
    @extend .has-error;
    .form-control {
    color: $state-danger-text;
    }
    }
    
  29. Note: Because both the presence validation and the has_secure_password validation catch the case of empty (nil) passwords, the signup form currently produces duplicate error messages when the user submits empty passwords. We could manipulate the error messages directly to eliminate duplicates, but luckily this issue will be fixed automatically by the addition of allow_nil: true in Section 9.1.4.
  30. redirect_to @userredirect_to user_url(@user) ,Rails知道這兩個是一樣的。
  31. The flash:出現後,在重新讀取頁面的時候就會消失了的功能。convention,用:success當作key。
    :success是key,將 "Welcome to the Sample App!"設定作value
    flash[:success] = "Welcome to the Sample App!"
    
    the flash, which we can treat like a hash. Rails adopts the convention of a :success key for a message indicating a successful result. 29.Flash會出現登入後的第一個頁面。 By assigning a message to the flash, we are now in a position to display the message on the first page after the redirect.a message that appears on the subsequent page and then disappears upon visiting a second page or on page reload.
  32. 把HTML和ERb結合看起來不是很漂亮的寫法。
    <% flash.each do |message_type, message| %>
    <div class="alert alert-<%= message_type %>"><%= message %></div>
    <% end %>
    
    alert-<%= message_type %>可以變成alert-success
    app/views/layouts/application.html.erb
     <!DOCTYPE html>
    <html>
      <% flash.each do |message_type, message| %>
        <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
      <% end %>
    </html>
    
  33. :success雖然是symbol,但是Rails 可以把它變成string "success" (The key :success is a symbol, but embedded Ruby automatically converts it to the string "success" before inserting it into the template.) 32.Bootstrap CSS:success, info, warning, and danger Using a different class for each key allows us to apply different styles to different kinds of messages. For example, in Section 8.1.4 we’ll use flash[:danger] to indicate a failed login attempt.10 (In fact, we’ve already used alert-danger once, to style the error message div in Listing 7.19.) Bootstrap CSS supports styling for four such flash classes (success, info, warning, and danger), and we’ll find occasion to use all of them in the course of developing the sample application.
  34. flash[:success] = "Welcome to the Sample App!" 會變成 <div class="alert alert-success">Welcome to the Sample App!</div>
  35. 全部就是這樣:
    app/views/layouts/application.html.erb
     <!DOCTYPE html>
    <html>
    <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div> 
    </body>
    </html>
    
  36. data離開local端後,容易受到攻擊(hijacking),所以使用Secure Sockets Layer (SSL)來克服。 When submitting the signup form developed in this chapter, the name, email address, and password get sent over the network, and hence are vulnerable to intercept. This is a potentially serious security flaw in our application, and the way to fix it is to use Secure Sockets Layer (SSL)11 to encrypt all relevant information before it leaves the local browser.
  37. 只要在production.rb加上一行 config.force_ssl = true,就可以開啟SSL的功能了 Enabling SSL is as easy as uncommenting a single line in production.rb, the configuration file for production applications. As shown in Listing 7.27, all we need to do is set the config variable to force the use of SSL in production.
    config/environments/production.rb
     Rails.application.configure do
    # Force all access to the app over SSL, use Strict-Transport-Security,
    
    # and use secure cookies.
    
    config.force_ssl = true
    end
    
  38. Setting up a production site to use SSL involves purchasing and configuring an SSL certificate for your domain. That’s a lot of work, though, and luckily we won’t need it here: for an application running on a Heroku domain (such as the sample application), we can piggyback on Heroku’s SSL certificate. As a result, when we deploy the application in Section 7.5.2, SSL will automatically be enabled. (If you want to run SSL on a custom domain, such as www.example.com, refer to Heroku’s page on SSL.) 36.WEBrick是純Ruby webserver,流量大時跑不動。production的時候應該要改用Puma,可以處理比較大的流量。 By default, Heroku uses a pure-Ruby webserver called WEBrick, which is easy to set up and run but isn’t good at handling significant traffic. As a result, WEBrick isn’t suitable for production use, so we’ll replace WEBrick with Puma, an HTTP server that is capable of handling a large number of incoming requests.
  39. puma gem,in the :production,要記得執行bundle install To add the new webserver, we simply follow the Heroku Puma documentation. The first step is to include the puma gem in our Gemfile, as shown in Listing 7.28. Because we don’t need the Puma gem locally, Listing 7.28 puts it in the :production group.
    Gemfile
    source 'https://rubygems.org'
    group :production do
    gem 'pg',             '0.17.1'
    gem 'rails_12factor', '0.0.2'
    gem 'puma',           '3.1.0'
    end
    
    Because we configured Bundler not to install production gems (Section 3.1), Listing 7.28 won’t add any gems to the development environment, but we still need to run Bundler to update Gemfile.lock:$ bundle install
  40. 新增一個config/puma.rb The next step is to create a file called config/puma.rb and fill it with the contents of Listing 7.29. The code in Listing 7.29 comes straight from the Heroku documentation,12 and there is no need to understand it.
    config/puma.rb
     workers Integer(ENV['WEB_CONCURRENCY'] || 2)
    threads_count = Integer(ENV['MAX_THREADS'] || 5)
    threads threads_count, threads_count
    preload_app!
    rackup      DefaultRackup
    port        ENV['PORT']     || 3000
    environment ENV['RACK_ENV'] || 'development'
    on_worker_boot do
    # Worker specific setup for Rails 4.1+
    
    # See: https://devcenter.heroku.com/articles/
    
    # deploying-rails-applications-with-the-puma-web-server#on-worker-boot
    
    ActiveRecord::Base.establish_connection
    end
    
  41. 在root新增一個Procfile,告訴Heroku要在production的時候使用PUMA。 Finally, we need to make a so-called Procfile to tell Heroku to run a Puma process in production, as shown in Listing 7.30. The Procfile should be created in your application’s root directory (i.e., in the same location as the Gemfile).
    ./Procfile Defining a Procfile for Puma.
    web: bundle exec puma -C config/puma.rb
    
  42. With the production webserver configuration completed, we’re ready to commit and deploy:13
    $ bundle exec rake test
    $ git add -A
    $ git commit -m "Use SSL and the Puma webserver in production"
    $ git push
    $ git push heroku
    $ heroku run rake db:migrate
    
  43. The signup form is now live, and the result of a successful signup is shown in Figure 7.24. Note the presence of https:// and a lock icon in the address bar of Figure 7.24, which indicate that SSL is working.
  44. When deploying to Heroku, you may get a warning message like this one:
    ###### WARNING:
       You have not declared a Ruby version in your Gemfile.
       To set your Ruby version add this line to your Gemfile:
       ruby '2.1.5'
    
    Experience shows that, at the level of this tutorial, the costs associated with including such an explicit Ruby version number outweigh the (negligible) benefits, so you should ignore this warning for now. The main issue is that keeping your sample app and system in sync with the latest Ruby version can be a huge inconvenience,14 and yet it almost never makes a difference which exact Ruby version number you use. Nevertheless, you should bear in mind that, should you ever end up running a mission-critical app on Heroku, specifying an exact Ruby version in the Gemfile is recommended to ensure maximum compatibility between development and production environments.
← ROR TUTORIAL (3RD ED.) Ch6 Modeling users box layout →