fangtingting
12/17/2017 - 7:51 AM

rails理论知识

rails基础理论知识

Rails MVC

Rails MVC:ActiveRecord(ORM实现数据库抽象)—ActionController—ActionView

Rails模块

1、action pack : 是一个独立的gem,包括以下三部分,是MVC 的VC层 – action_controller – action_dispatch : 处理web请求的路由等 – action_view

2、active model:在 Action Pack gem 和 ORM gem (例如 Active Record) 之间定义了一組介面。Active Model 允許 Rails 可以依你的需求把 Active Record 換成其他 ORM 框架。

3、active record:是 Rails 應用程式中的 Models基礎。它不依存特定的資料庫系統,提供了 CRUD 功能、先進的查詢能力以及可以跟其他 Models 關聯的本事。

4、active support : 工具类和ruby标准库的扩展

5、action mailer : 收发邮件模块

6、railties :是rails framework的核心,用来提供各种回调方法以及扩展,或者修改初始化的process. 实际上每个rails组件(ActionMailer,ActionController)都是一个railtie, 每个组件都有自己的初始化代码,这样rails就不用去专门把初始化工作都自己来做,只要在正确的时候调用各组件的初始化代码就可以了。

7、rack构建rails中间件:rack提供了用ruby开发web应用的一个借口。

8、rails middleware:rake middleware命令可查看rails的中间件。

Active Record

1、Active Record 是 MVC 中的 M(模型),处理数据和业务逻辑。Active Record 负责创建和使用需要持久存入数据库中的数据。Active Record 实现了 Active Record 模式,是一种对象关系映射系统。

2、对象关系映射(ORM)是一种技术手段,把程序中的对象和关系型数据库中的数据表连接起来。使用 ORM,程序中对象的属性和对象之间的关系可以通过一种简单的方法从数据库获取,无需直接编写 SQL 语句,也不过度依赖特定的数据库种类。

数据库迁移

迁移使用一种统一、简单的方式,按照时间顺序修改数据库的模式。迁移使用 Ruby DSL 编写,因此不用手动编写 SQL 语句,对数据库的操作和所用的数据库种类无关。

你可以把每个迁移看做数据库的一个修订版本。数据库中一开始什么也没有,各个迁移会添加或删除数据表、字段或记录。Active Record 知道如何按照时间线更新数据库,不管数据库现在的模式如何,都能更新到最新结构。同时,Active Record 还会更新 db/schema.rb 文件,匹配最新的数据库结构

数据库验证

数据存入数据库之前的验证方法还有其他几种,包括数据库内建的约束,客户端验证和控制器层验证。下面列出了这几种验证方法的优缺点:

1、数据库约束和“存储过程”无法兼容多种数据库,而且测试和维护较为困难。不过,如果其他程序也要使用这个数据库,最好在数据库层做些约束。数据库层的某些验证(例如在使用量很高的数据表中做唯一性验证)通过其他方式实现起来有点困难。

2、客户端验证很有用,但单独使用时可靠性不高。如果使用 JavaScript 实现,用户在浏览器中禁用 JavaScript 后很容易跳过验证。客户端验证和其他验证方式结合使用,可以为用户提供实时反馈。

3、控制器层验证很诱人,但一般都不灵便,难以测试和维护。只要可能,就要保证控制器的代码简洁性,这样才有利于长远发展。 你可以根据实际的需求选择使用哪种验证方式。Rails 团队认为,模型层数据验证最具普适性。

什么时候做数据验证?

在 Active Record 中对象有两种状态:一种在数据库中有对应的记录,一种没有。新建的对象(例如,使用 new 方法)还不属于数据库。在对象上调用 save 方法后,才会把对象存入相应的数据表。Active Record 使用实例方法 new_record? 判断对象是否已经存入数据库。一般情况下,数据验证发生在这些 SQL 操作执行之前。如果验证失败,对象会被标记为不合法,Active Record 不会向数据库发送 INSERT 或 UPDATE 指令。这样就可以避免把不合法的数据存入数据库。你可以选择在对象创建、保存或更新时执行哪些数据验证。

Active Record 回调

在 Rails 程序运行过程中,对象可以被创建、更新和销毁。Active Record 为对象的生命周期提供了很多钩子,让你控制程序及其数据。回调是在对象生命周期的特定时刻执行的方法。回调方法可以在 Active Record 对象创建、保存、更新、删除、验证或从数据库中读出时执行。

Action Controller会话

程序中的每个用户都有一个会话(session),可以存储少量数据,在多次请求中永久存储。会话只能在控制器和视图中使用,可以通过以下几种存储机制实现:

ActionDispatch::Session::CookieStore:所有数据都存储在客户端

ActionDispatch::Session::CacheStore:数据存储在 Rails 缓存里

ActionDispatch::Session::ActiveRecordStore:使用 Active Record 把数据存储在数据库中(需要使用 activerecord-session_store gem)

ActionDispatch::Session::MemCacheStore:数据存储在 Memcached 集群中(这是以前的实现方式,现在请改用 CacheStore) 所有存储机制都会用到一个 cookie,存储每个会话的 ID(必须使用 cookie,因为 Rails 不允许在 URL 中传递会话 ID,这么做不安全)。

大多数存储机制都会使用这个 ID 在服务商查询会话数据,例如在数据库中查询。不过有个例外,即默认也是推荐使用的存储方式 CookieStore。CookieStore 把所有会话数据都存储在 cookie 中(如果需要,还是可以使用 ID)。CookieStore 的优点是轻量,而且在新程序中使用会话也不用额外的设置。cookie 中存储的数据会使用密令签名,以防篡改。cookie 会被加密,任何有权访问的人都无法读取其内容。(如果修改了 cookie,Rails 会拒绝使用。)

CookieStore 可以存储大约 4KB 数据,比其他几种存储机制都少很多,但一般也足够用了。不过使用哪种存储机制,都不建议在会话中存储大量数据。应该特别避免在会话中存储复杂的对象(Ruby 基本对象之外的一切对象,最常见的是模型实例),服务器可能无法在多次请求中重组数据,最终导致错误。

如果会话中没有存储重要的数据,或者不需要持久存储(例如使用 Falsh 存储消息),可以考虑使用 ActionDispatch::Session::CacheStore。这种存储机制使用程序所配置的缓存方式。CacheStore 的优点是,可以直接使用现有的缓存方式存储会话,不用额外的设置。不过缺点也很明显,会话存在时间很多,随时可能消失。

Active Support扩展

除非把config.active_support.bare设置为 true, 否则 Ruby on Rails 的程序会加载全部的 Active Support。

1、所有对象都可用的扩展:blank? 、present?、try、send、class_eval(*args, &block)、acts_like?(duck)、to_param、with_option

  • blank? and present?
  • try:try就好比Object#send,只不过如果接收者为nil,那么返回值也会是nil
  • class_eval(*args, &block):使代码在对象的单件类的上下文里执行
  • acts_like?(duck):acts_like?方法可以用来判断某个类与另一个类是否有相同的行为
  • to_param:把对象的值转换为查询字符串,或者 URL 片段,并返回该值

2、实例变量访问

  • instance_values:返回一个散列表,其中会把实例变量名去掉"@"作为键,把相应的实例变量值作为值
  • instance_variable_names:返回一个数组。数组中所有的实例变量名都带有"@"标志
  • local_constants:返回在接收者模块中定义的常量

3、字符串扩展

  • html_safe

  • truncate(length):截断字符串

  • pluralize:转复数

  • singularize:转单数

  • camelize:字符串转换成驼峰

    "product".camelize # => "Product" "admin_user".camelize # => "AdminUser" "backoffice/session".camelize # => "Backoffice::Session"

  • underscore:与camelize相反

    "Product".underscore # => "product" "AdminUser".underscore # => "admin_user" "Backoffice::Session".underscore # => "backoffice/session"

  • titleize:字符串标题化,单字开头大写

  • dasherize:下划线转”-”

    "name".dasherize # => "name" "contact_data".dasherize # => "contact-data"

  • demodulize:

    "Product".demodulize # => "Product" "Backoffice::UsersController".demodulize # => "UsersController" "Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils" "::Inflections".demodulize # => "Inflections","".demodulize # => ""

  • parameterize: "John Smith".parameterize # => "john-smith","Kurt Gödel".parameterize # => "kurt-godel"

  • tableize:表格化,符串转换成表的名字,单数变复数。

    "Person".tableize # => "people" "Invoice".tableize # => "invoices" "InvoiceLine".tableize # => "invoice_lines"

  • classify:字符串转成类,与tableize相反

    "people".classify # => "Person" "invoices".classify # => "Invoice" "invoice_lines".classify # => "InvoiceLine"

  • constantize:常量引用表达式,将字符串常量化转换成类或模型

    "Fixnum".constantize # => Fixnum

4、扩展属性

rails提供定义别名 alias_attribute :login, :email

rails提供快速定义访问同名实例变 attr_accessible :name

rails提供模型的属性访问,mattr_reader、mattr_writer和mattr_accessor与为类定义的cattr_*是相同的 module ActiveSupport module Dependencies mattr_accessor :warnings_on_first_load mattr_accessor :history end end

5、数组、hash扩展

  • split

  • [0, 1, -5, 1, 1, "foo", "bar"].split(1)# => [[0], [-5], [], ["foo", "bar"]]

  • merge:合并两个hash

  • {a: 1, b: 1}.merge(a: 0, c: 2) # => {:a=>0, :b=>1, :c=>2}

  • except and except!

  • {a: 1, b: 2}.except(:a) # => {:b=>2}

  • stringify_keys and stringify_keys!

  • {nil => nil, 1 => 1, a: :a}.stringify_keys # => {"" => nil, "a" => :a, "1" => 1}

  • symbolize_keys and symbolize_keys!

  • {nil => nil, 1 => 1, "a" => "a"}.symbolize_keys # => {1=>1, nil=>nil, :a=>"a"}

  • compact and compact!: return a Hash without items with nil value

  • {a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}

  • include?

Asset Pipeline

Asset Pipeline 提供了一个框架,用于连接、压缩 JavaScript 和 CSS 文件。还允许使用其他语言和预处理器编写 JavaScript 和 CSS,例如 CoffeeScript、Sass 和 ERB。严格来说,Asset Pipeline 不是 Rails 4 的核心功能,已经从框架中提取出来,制成了 sprockets-rails gem。Asset Pipeline 功能默认是启用的。启用Asset Pipeline,Rails 4 会自动把 sass-rails、coffee-rails 和 uglifier 三个 gem 加入 Gemfile,Sprockets 使用这三个 gem 压缩静态资源。

新建程序时如果想禁用 Asset Pipeline,指定 --skip-sprockets 命令行选项后,Rails 4 不会把 sass-rails 和 uglifier 加入 Gemfile。如果后续需要使用 Asset Pipeline,需要手动添加这些 gem。而且,指定 --skip-sprockets 命令行选项后,生成的 config/application.rb 文件也会有点不同,把加载 sprockets/railtie 的代码注释掉了

production.rb 文件中有相应的选项设置静态资源的压缩方式:config.assets.css_compressor 针对 CSS,config.assets.js_compressor 针对 Javascript。如果 Gemfile 中有 sass-rails,就会自动用来压缩 CSS,无需设置 config.assets.css_compressor 选项。 config.assets.css_compressor = :yui config.assets.js_compressor = :uglify

1、链接静态资源 在以前的 Rails 版本中,所有静态资源都放在 public 文件夹的子文件夹中,例如 images、javascripts 和 stylesheets。使用 Asset Pipeline 后,建议把静态资源放在 app/assets 文件夹中。这个文件夹中的文件会经由 Sprockets 中间件处理。默认情况下,在生产环境中,Rails 会把预先编译好的文件保存到 public/assets 文件夹中,网页服务器会把这些文件视为静态资源。在生产环境中,不会直接伺服 app/assets 文件夹中的文件。或者通过命令rake assets:precompile将app/assets 文件夹中的文件进行编译。

针对控制器的样式表和 JavaScript 文件也可只在相应的控制器中引入 <%= javascript_include_tag params[:controller] %> <%= stylesheet_link_tag params[:controller] %>

#如果使用 Turbolinks(Rails 4 默认启用),加上 data-turbolinks-track 选项后,Turbolinks 会检查静态资源是否有更新,如果更新了就会将其载入页面 <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %>

#在 config/application.rb 文件中加入以下代码可以禁止生成控制器相关的静态资源 config.generators do |g| g.assets false end

2、静态资源组织方式

Asset Pipeline 的静态文件可以放在三个位置:app/assets,lib/assets 或 vendor/assets。

  • app/assets:存放程序的静态资源,例如图片、JavaScript 和样式表;
  • lib/assets:存放自己的代码库,或者共用代码库的静态资源;
  • vendor/assets:存放他人的静态资源,例如 JavaScript 插件,或者 CSS 框架;

在清单文件或帮助方法中引用静态资源时,Sprockets 会在默认的三个位置中查找对应的文件。默认的位置是 /assets 文件夹中的 images、javascripts 和 stylesheets 三个子文件夹。这三个文件夹没什么特别之处,其实 Sprockets 会搜索 /assets 文件夹中的所有子文件夹。Sprockets 会按照搜索路径中各路径出现的顺序进行搜索。默认情况下,这意味着 app/assets 文件夹中的静态资源优先级较高,会遮盖 lib 和 vendor 文件夹中的相应文件。

清单文件中引用以下静态资源,Sprockets 通过清单文件决定要引入和伺服哪些静态资源,也是通过清单文件编译静态资源 app/assets/javascripts/home.js lib/assets/javascripts/moovinator.js vendor/assets/javascripts/slider.js vendor/assets/somepackage/phonebox.js

//= require home //= require moovinator //= require slider //= require phonebox

#除了标准的 assets/* 路径之外,还可以在 config/application.rb 文件中向 Asset Pipeline 添加其他路径。 config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")

3、asset_path帮助方法 Asset Pipeline 会自动执行 ERB 代码,所以如果在 CSS 文件名后加上扩展名 erb(例如 application.css.erb),那么在 CSS 规则中就可使用 asset_path 等帮助方法。

.class { background-image: url(<%= asset_path 'image.png' %>) } #非erb文件要用下面提供的方法 image-url("rails.png") 编译成 url(/assets/rails.png) image-path("rails.png") 编译成 "/assets/rails.png" asset-url("rails.png") 编译成 url(/assets/rails.png) asset-path("rails.png") 编译成 "/assets/rails.png"

4、编译静态资源 Rails 提供了一个 rake 任务用来编译清单文件中的静态资源和其他相关文件。命令:RAILS_ENV=production bundle exec rake assets:precompile。引用文件时,会使用加上 MD5 哈希的文件名代替清单文件中的名字。

默认编译的文件包括 application.js和application.css清单,以及 gem 中 app/assets 文件夹中的所有非 JS/CSS 文件(会自动加载所有图片)。如果想编译其他清单,或者单独的样式表和 JavaScript,可以添加到 config/application.rb 文件中的 precompile 选项 config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']

或者可以按照下面的方式,设置编译所有静态资源 config.assets.precompile << Proc.new do |path| if path =~ /.(css|js)\z/ full_path = Rails.application.assets.resolve(path).to_path app_assets_path = Rails.root.join('app', 'assets').to_path if full_path.starts_with? app_assets_path puts "including asset: " + full_path true else puts "excluding asset: " + full_path false end else false end end

5、gem中使用静态资源 为 Rails 提供标准 JavaScript 代码库的 jquery-rails gem 是个很好的例子。这个 gem 中有个引擎类,继承自 Rails::Engine。添加这层继承关系后,Rails 就知道这个 gem 中可能包含静态资源文件,会把这个引擎中的 app/assets、lib/assets 和 vendor/assets 三个文件夹加入 Sprockets 的搜索路径中。

6、升级旧版本rails 从 Rails 3.1 开始,jQuery 是默认的 JavaScript 库,因此不用把 jquery.js 复制到 app/assets 文件夹中。Rails 会自动加载 jQuery。

Rails中使用JavaScript

Rails 默认支持 CoffeeScript,后文所有的示例都用 CoffeeScript 编写。

#CoffeeScript使用jquery发起Ajax请求 $.ajax(url: "/test").done (html) -> $("#results").append html

1、内建方法 通过remote选项实现ajax,跳到show action里执行代码返回js,创建视图show.js.erb link_to “url”,show_path(obj),remote: true #show.js.erb $("<%= escape_javascript(render @user) %>").appendTo("#users"); $("#newModal").html("<%= j(render('form_modal',:this => this))%>");

2、Turbolinks 要想使用 Turbolinks,只需将其加入 Gemfile,然后在 app/assets/javascripts/application.js 中加入 //= require turbolinks 即可。

Rails on Rack

Rack 为使用 Ruby 开发的网页程序提供了小型模块化,适应性极高的接口。Rack 尽量使用最简单的方式封装 HTTP 请求和响应,为服务器、框架和二者之间的软件(中间件)提供了统一的 API,只要调用一个简单的方法就能完成一切操作。文档:http://rack.rubyforge.org/doc/

1、Action Dispatcher 中间件 Action Dispatcher 中的很多组件都以 Rack 中间件的形式实现。Rails::Application 通过 ActionDispatch::MiddlewareStack 把内部和外部的中间件组合在一起,形成一个完整的 Rails Rack 程序。 查看使用的中间件:rake middleware

2、配置中间件 Rails 在 application.rb 或 environments/environment.rb 文件中提供了一个简单的设置项 config.middleware,可以在middleware堆栈中添加,修改和删除中间件 。

config/application.rb

#把新中间件添加到列表末尾 config.middleware.use Rack::BounceFavicon

#在 existing_middleware 之前添加新中间件 config.middleware.insert_before(existing_middleware, new_middleware, args)

#在 existing_middleware 之后添加新中间件 config.middleware.insert_after(existing_middleware, new_middleware, args) config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, page_cache: false

#替换中间件

Replace ActionDispatch::ShowExceptions with Lifo::ShowExceptions

config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions

#删除中间件 config.middleware.delete "Rack::Lock"

#删除会话相关的中间件 config.middleware.delete "ActionDispatch::Cookies" config.middleware.delete "ActionDispatch::Session::CookieStore" config.middleware.delete "ActionDispatch::Flash"

#删除浏览器相关的中间件 config.middleware.delete "Rack::MethodOverride"

3、内部中间件 Action Controller 的很多功能都以中间件的形式实现。下面解释个中间件的作用。

Rack::Sendfile:设置服务器上的 X-Sendfile 报头。通过 config.action_dispatch.x_sendfile_header 选项设置。

ActionDispatch::Static:用来服务静态资源文件。如果选项 config.serve_static_assets 为 false,则禁用这个中间件。

Rack::Lock:把 env["rack.multithread"] 旗标设为 false,程序放入互斥锁中。

ActiveSupport::Cache::Strategy::LocalCache::Middleware:在内存中保存缓存,非线程安全。

Rack::Runtime:设置 X-Runtime 报头,即执行请求的时长,单位为秒。

Rack::MethodOverride:如果指定了 params[:_method] 参数,会覆盖所用的请求方法。这个中间件实现了 PUT 和 DELETE 方法。

ActionDispatch::RequestId:在响应中设置一个唯一的 X-Request-Id 报头,并启用 ActionDispatch::Request#uuid 方法。

Rails::Rack::Logger:请求开始时提醒日志,请求完成后写入日志。

ActionDispatch::ShowExceptions:补救程序抛出的所有异常,调用处理异常的程序,使用特定的格式显示给用户。

ActionDispatch::DebugExceptions:如果在本地开发,把异常写入日志,并显示一个调试页面。

ActionDispatch::RemoteIp:检查欺骗攻击的 IP。

ActionDispatch::Reloader:提供“准备”和“清理”回调,协助开发环境中的代码重新加载功能。

ActionDispatch::Callbacks:在处理请求之前调用“准备”回调。

ActiveRecord::Migration::CheckPending:检查是否有待运行的迁移,如果有就抛出 ActiveRecord::PendingMigrationError 异常。

ActiveRecord::ConnectionAdapters::ConnectionManagement:请求处理完成后,清理活跃的连接,除非在发起请求的环境中把 rack.test 设为 true。

ActiveRecord::QueryCache:启用 Active Record 查询缓存。

ActionDispatch::Cookies:设置请求的 cookies。

ActionDispatch::Session::CookieStore:负责把会话存储在 cookies 中。

ActionDispatch::Flash:设置 Flash 消息的键。只有设定了 config.action_controller.session_store 选项时才可用。

ActionDispatch::ParamsParser:把请求中的参数出入 params。

ActionDispatch::Head:把 HEAD 请求转换成 GET 请求,并处理。

Rack::ConditionalGet:添加对“条件 GET”的支持,如果页面未修改,就不响应。

Rack::ETag:为所有字符串类型的主体添加 ETags 报头。ETags 用来验证缓存。