Rails学习笔记


本文是一个Rails新手的学习笔记,主要是对过去一个月中学习内容的总结,包括:css

  • Agile Web Development with Rails 4html

  • Rails 101web

  • Rails for Zombies算法

水平有限, 错误再所不免(这也是我写出来的缘由啦 :D), 还请诸位多多指教.数据库

学习体验

Rails 的开发速度很是快,但学习速度是很慢的。DHH曾经提到过:相比learnability,他更看重usability。DHH的15分钟开发博客程序确实激动人心,可是不通过全面的学习,想要作出合格可用的产品是不现实的。bootstrap

概述

学习Rails究竟是在学习什么? 在我看来,主要是如下几个:数组

了解处理用户请求的完整路径

这是学习任何一个web框架,都要完成的任务。Rails相比其余框架,须要学习的点就是MVC 和 routes 文件的写法。浏览器

MVC

Rails框架强制你使用MVC模式进行开发,那什么是真正的MVC呢?那就是:ruby

用户请求的永远是controller,服务器返回给浏览器的永远是view.服务器

以用户注册为例,直觉的作法是用户点击首页的注册连接,转到有表格的view,用户填写信息以后submit,请求转到controller中的特定action,处理请求以后根据结果转回注册页面或者自动登陆到首页。

这种作法并无彻底符合MVC,由于有经过硬编码的连接实现的view到view的跳转,Rails的作法是这样的:

点击首页的注册连接,跳转到users_controller的 new,这个action计算用户须要填写的信息,初始化一个空的对象,转到new_html这个view,用户填写表格以后submit,请求转给create这个action,处理请求以后根据结果 redirect_to 到 new (重定向会让浏览器再次向服务器发起对controller下 new 的请求)或者自动登陆到首页。

永远是 view -> controller -> view 的处理流程。

这并非一个好例子,或许我该举一些“不在view中写业务逻辑、保持controller精简”之类的最佳实践,可是它们都被举烂了,我也就再也不多说,我举得这个例子或许能让你对MVC有多一些的思考。

routes

routes.rb这个文件无疑是任何 Rails 项目的灵魂, 我获得的建议是: 若是你打算研究一个 Rails 开源项目, 要打开的第二个文件永远是routes.rb, 第一个固然是Gemfile啦, :D.

这个文件的两个基本做用:

  • 提供了new_user_path这样的写法,避免出现硬编码的连接
  • 把用户地址栏的连接与controller中的action相对应.

这就牵扯到了Rrails的一个重要特性: restful. 之前写Servlet, 常常会有/getUser?id=3这样的连接, restful的想法是, 既然自己已是GET请求了, 连接何须再有get呢. Rails就按照restful的思路, 把连接变成了: GET users/:id. 在使用该连接的时候, 用users_path(@user)这样的写法来传递id这个参数.

restful固然还有更"高大上"的做用, 例如分布式系统下的容错性之类的, 可是对于新手, 这样理解设计者的初衷就行了: 就是将updateUserInfo?id=3 deleteUser?id=4这样的连接, 转成PUT users/3 DELETE users/3.

新手常常搞不清楚path的写法, 尤为是出现各类nested resources的时候, 在终端输入rake routes, 就能够看到了正确写法了.

ORM

主要学习的点包括migration, seeds.rb, 表关系及查询语句的编写.

migration

SQL无疑是很是好用的语言, 但有个致命的肯定: 三五人的小团队, 没有专门的DBA, 如何保证全部开发者的数据库设计是一致的? 固然, 有许多的作法能够解决这个问题, 这些作法中migration是最好的之一.

它的基本思路, 就是用ruby代码来作数据库设计(增长新的表, 增长索引, 改变某列的数据类型), 每次修改都放在单独的良好命名的文件之中, 只要将其归入版本管理, 经过rake db:migrate命令, 就能够保证全部开发者的数据库设计师一致的.

同时借助seeds.rb文件, 保证全部开发者的测试数据是一致的.

表关系

也就是表之间的主外键关系以及三范式的知足. 在我短暂的Rails开发旅程中, 大多数时间都花费在数据库设计上, 也使我对

程序 = 算法 + 数据结构

这一论断愈来愈信服.

Rails 提供了诸多功能方便开发者申明表之间的主外键关系, 包括has_many belongs_to has_many_through等, 合理地利用该特性能够帮助你写出可读性很是好的代码, 例如在这样的主外键设计之下:

class Group < ActiveRecord::Base
    has_many :group_users
    has_many :members, :through => :group_users, :source => :user 
end

class GroupUser < ActiveRecord::Base
    belongs_to :user
    belongs_to :group
end

class User < ActiveRecord::Base
    has_many :group_users
    has_many :participated_groups, :through => :group_users, :source => :group
end

能够用current_user.participated_groups这样十分天然的语句来获取当前登陆用户参加的社团.

查询语句

这一点没什么好说的, 就两点:

  • 注意避免一些明显的错误, 例如N+1
  • where返回的是数组,find_by返回的是找到的第一个值

经常使用gem的使用

这也是学习Rails中的很重要的一个点, 选择Rrails是为了快速开发, 那为何不更快一点呢. devise让你一份实现登录与注册, 邮箱验证, 密码找回; bootstrap-rails让你两分钟提高网站的设计感; will_paginate让你再也不操心分页的实现....

其余

固然还有许多重要的点要学习, 可是都没有上面几个那样有提纲挈领的做用. 都放到下一节当细节说吧.

tips

工具

主要就是Sublime Text了, 有几个插件很是好用, 明显提升效率. 我装了Emmet, ERB SnippetsRuby on Rails snippets三个插件, 主要是实现

  • PE而后tab 能够自动生成<%= -%>
  • ER而后tab 能够自动生成<% %>
  • ul>li*2>a而后tab能够打出:
<li><a href=""></a></li>
<li><a href=""></a></li>
  • 还有许多神奇的功能, has_many之类的, 你们自行查看其官网的cheat_sheet

ORM

scope的使用

scope :graveyard, where(:show_location => true , :location => 'graveyard')

add_index

给列添加索引, 以提高性能

主外键关系的一些额外属性

  • 能够显示地设置外键
class Tweet < ActiveRecord::Base
  has_one :location, :dependent => :destroy, :foreign_key => :tweeter_id
end
class Location < ActiveRecord::Base
  belongs_to :tweet, :foreign_key => :tweeter_id
end
  • dependent: :destroy来设置外键依赖
  • 避免 N+1 issue
    使用 Zombie.includes(:brain).all ,来代替直接Zombie.all, 而后view中each打印zombie.brain, 以提高性能

  • update_attributes和update是同样的

rake的使用

参考这里 rake, 也可使用rake -T查看

  • 使用rake db:schema:dump, 能够从如今的数据库建立schema文件
  • 学习开源项目时, 使用rake db:setup. 三件事: 建立数据库, 读取schema文件, 从'seeds.rb'导入种子数据

controller

统一处理各action中的异常

  • module定义在concerns/current_cart_rb中, 一方面是其中的方法在各个controller中共享, 其次防止其中的方法被当作action来调用. 注意该文件夹下的文件都是autoload的, 不须要显式地写include.
  • 注意是如何使用callback使set_cart执行的.
class CartsController < ApplicationController
  before_action :set_cart, only: [:show, :edit, :update, :destroy]
  rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart 

  def index
    @carts = Cart.all
  end

  private
    def invalid_cart
      logger.error { "Attempt to access invalid cart #{params[:id]}" }
      redirect_to store_path, notice: 'Invalid cart!'
    end
end

module CurrentCart
    extend ActiveSupport::Concern

    private

    def set_cart
        @cart = Cart.find(session[:cart_id])
    rescue ActiveRecord::RecordNotFound
        @cart = Cart.create
        session[:cart_id] = @cart.id
    end
end

防止doble render

def new
    if @cart.line_items.empty?
      redirect_to store_url, notice: 'Your cart is empty.'
      return
      #redirect_to不会自动return
    end

    @order = Order.new
  end

一些澄清

-before_filter 就是旧版本中的 before_action
- build 就是new的别名
- save!会引起RecordInvalid异常, 而save会返回boolean
- nil?判断对象是否存在, 不存在的对象都是nil的; empty?判断是否为空字段,好比一个字符串是否为空串,或者一个数组中是否有值;object.blank? 至关于o
bject.nil?||object.empty?`
- 在befor类的钩子函数中,返回FALSE则不执行before后跟的操做, 此处容易出bug

model

错误信息

能够用errors.add(:base, 'Line Items present')添加错误信息, 在controller中能够经过@line_item.errors来获取.

view

body的class

能够经过<body class='<%= controller.controller_name %>'>来命名body的class, 方便scss的书写.

select的实现

#view中
<%= f.select :pay_type, Order::PAYMENT_TYPES, prompt: 'Select a payment method' %>
# model中
PAYMENT_TYPES = ['Check', 'Credit Card', 'Purchase Order']
validates :pay_type, inclusion: PAYMENT_TYPES

一些澄清

  • button_to默认是POST请求, link_to默认是GET请求.
相关文章
相关标签/搜索