4-10 辅助方法controll_name,;SanitizeHelper; 伪元素和scss中的&, @Media; cache介绍。

回顾知识点

 

1. 在application.html.erb中: <main class="<%= controller.controller_name%>"> 的controller.controller_name是怎么理解?

 

api:ActionController::Metal中的实例方法controller_name,

def controller_name

  self.class.controller_name   #=> 里面调用同名类方法

end 

思考:应用模版是根据请求的controller来渲染的,所以这里的controller应该是products.

尝试:在products/index.html.erb中,输出 <h1><%= controller %></h1>得到:实例化的对象#<ProductsController:0x00007fafcc72d070>,所以它的类是ProductsController,然后调用同名类方法,看源码,就是用正则表达式返回string。

def self.controller_name
@controller_name ||= name.demodulize.sub(/Controller$/, "").underscore
end 

解析: ProductsController.name => "ProductsController"

   "ProductsController"  => 去掉继承的父类字符,还是 "ProductsController"

 然后调用sub()把最后的字符串,替换为空。 => "Product"

         再用underscore(),在大写字符前加下划线,然后改成小写 => "product"

结论: <main class="<%= controller.controller_name%>">相当于<main class="product">,调用css样式,对应在products.scss

 

 


 

 

ActionView::Helpers::SanitizeHelper(消毒,净化)

 The SanitizeHelper module provides a set of methods for clean text of undesired HTML elements. These helper methods extend Action View making them callable within your template files. 4个方法。

sanitize(html, options = {}) ,可以加白名单

 


scss:用于编译成html认识的css (详细)

li { 

      ... 

      &::after {
        clear:both;
        content: " ";
        display: block;
      }

complied:

li {...}

li::after{...} 

::after是psesudo element 一般用于在某个元素后面添加content:"xxx"内容,这里应该是用在li的循环在li之间加一个空行。

The ampersand & in SASS pulls in the parent selector when it compiles to CSS.
 

什么是css伪元素A CSS pseudo-element is a keyword added to a selector that lets you style a specific part of the selected element(s). For example, ::first-line can be used to change the font of the first line of a paragraph

/* The first line of every <p> element. */ 
p::first-line { color: blue; text-transform: uppercase; }

 

Responsive Web Design - Media Queries

Media query is a CSS technique introduced in CSS3.

It uses the @media rule to include a block of CSS properties only if a certain condition is true.

 

 

 
Desktop
Tablet
      

 

 Phone

 

 

 

 


 

勘误《Rails5敏捷开发》:cache  P115 缓存局部结果

 缺少一段翻译,和关键的一行代码:5.1版原文如下:


As far as verifying that this works is concerned, you’re going to get some insight into the work the server is doing behind the scenes. Go back to your server window and watch what happens when you refresh the page. The first time you load the page, you should see some SQL that is loading the products like Product Load (0.2ms) SELECT "products".* FROM "products" ORDER BY "products"."title" ASC. When you refresh the page again, it will still work, but you won’t see that SQL run. You should see some SQL that Rails runs to check if its cache is outdated, like so: SELECT COUNT(*) AS "size", MAX("products"."updated_at") AS timestamp FROM "products".
If you still aren’t convinced, you can add a configuration option to config/environments/development.rb called enable_fragment_cache_logging, like so:
​# Enable/disable caching. By default caching is disabled.​
​if​ Rails.root.join(​'tmp/caching-dev.txt'​).exist?
»   config.action_controller.enable_fragment_cache_logging = ​true​
  config.action_controller.perform_caching = ​true​
You’ll need to restart your server for this to take effect, but after doing that, you should see log messages that look like this:
Read fragment views/products/4-20180409121532794883/382d59eb4358e2ff3dde9ec3a35ca386 (0.0ms)
Write fragment views/products/4-20180409121532794883/382d59eb4358e2ff3dde9ec3a35ca386 (0.0ms)
Write fragment views/products/query-b41f6fbd1a3876c26c349b7e6dd13d56-3-20180409121532797255/382d59eb4358e2ff3dde9ec3a35ca386 (0.0ms)

 


 

⚠️: 这篇博客有详细的案例讲解HTTP cache, fragment cache:

 

 

rails默认提供fragment caching

action caching和page caching需要手动设置:

增加2个gem:actionpack-page_caching和actionpack-action_caching 具体做法需要看文档的连接。

 

默认情况下,缓存只在生产环境启动,如果想在本地启动缓存,需要:

在config/environments/*.rb中一般是developemnt.rb中: 

config.action_controller.enable_fragment_cache_logging = ​true​

如代码所写,缓存只影响action controller,对底层缓存无影响。 

 

片段缓存:

 

“片段缓存把视图逻辑的一部分放在 cache 块中,下次请求使用缓存存储器中的副本伺服。”

首次访问这个页面时,Rails会创建 一个时间戳(对象的id加updated_at属性),如果updated_at变化了则生成新的键然后写入一个新的缓存。

cache_if/cache_unless方法可以用于特定的条件。 

 

示例:集合缓存,一次性缓存,速度更快。

<%= render partial:'products/product', collection:@products, cache: true %> 

示例:片段缓存, 

<% @products.each do |product| %> 

  <% cache product do %>
    <%= render product %>
  <% end %>
<% end %> 

 

rails dev:cache 在开发/测试环境打开/关闭缓存

      在关联的模型上,如果视图有嵌套,在belongs_to 后加,touch:true,同步判断缓存是否失效。

      有时,可能想把缓存的片段嵌套在其他缓存的片段里。这叫俄罗斯套娃缓存(Russian doll caching)。 俄罗斯套娃缓存的优点是,更新单个商品后,重新生成外层片段时,其他内存片段可以复用。

      如果缓存的文件对应的记录的 updated_at 属性值变了,缓存的文件失效。但是,内层嵌套的片段不失效。

对下面的视图来说:

<% cache product do %>
  <%= render product.games %>
<% end %>

而它渲染这个视图:

<% cache game do %>
  <%= render game %>
<% end %>”
 

如果game的任何一个属性变了,updated_at 的值会设为当前时间,因此缓存失效。然而,project对象的 updated_at 属性不变,因此它的缓存不失效,从而导致应用伺服过期的数据。为了解决这个问题,可以使用 touch 方法把模型绑在一起:

class Product < ApplicationRecord
  has_many :games
end
class Game < ApplicationRecord
  belongs_to :product, touch: true
end 
这样就同步失效了。目的是让

管理依赖:有时候对需要处理自定义的helper methods,要自定义缓存依赖.

比如自定义的集合:
render @project.documents.where(published: true)
需要改为标准格式:
render partial: "documents/document", collection: @project.documents
  .where(published: true), cache:true

Explicit dependency
External dependency
这两个没有弄明白。http://guides.rubyonrails.org/caching_with_rails.html#shared-partial-caching


底层缓存:

缓存特定的值或者query结果。使用Rails.cache.fetch方法 ,详细用法看api.

ActiveSupport::Cache::Store可以储存任何可串联的Ruby对象。

基本的缓存方法: 

fetchwritereadexist?, and delete

 keys会被转化为string.

 cache = ActiveSupport::Cache::MemoryStore.new

 cache.write("city", "Duckburgh")

 cache.read("city")  #=> "Duckburgh" 

 

fetch(name, options = nil)

使用给定的key从缓存取数据。如果有对应的数据,返回这个数据。

如果没有对应的数据,返回nil。

如果一个block被传入, key先在缓存中找对应数据,如果找不到,则把block中的数据作为这个key的数据写入缓存,并返回block中的数据。

 

option选择有多个,如expires_in: 12.hours 在12小时后缓存到期.  namespace则用于创建命名空间防止与其他应用共用一个缓存存储器。

 

下面是个Product模型,有个实例方法,在竞争网站查找某个商品的价格。 

 

class Product < ApplicationRecord
  def competing_price
    Rails.cache.fetch("#{cache_key}/competing_price", expires_in: 12.hours) do
      Competitor::API.find_price(id)
    end
  end
end 

  


SQL缓存

Rails提供的功能,如果在同一请求中遇到相同的查询结果,rails会使用缓存的结果。⚠️的是SQL缓存机制只在一个动作内有效,如果这个动作结束,缓存也就销毁了。如果想持久储存查询结果,可以使用👆讲的底层缓存。

 


 

 

缓存存储器

 ActiveSupport::Cache::Store这是基础。

 

 ActiveSupport::Cache::MemCacheStore < ActiveSupport::Cache::Store

在生产环境的网站目前流行使用的缓存存储器。初始化使用的时候,需要指定服务器的地址(具体见rails guide.) fetch和write有额外的option。

需要在config/environments/production.rb中:

config.cache_store = :mem_cache_stor 

这个存储器可以和自动到期缓存良好的一起运行。 

 

ActiveSupport::Cache::FileStore  缓存使用文件系统存储缓存条目:

config.cache_store = :file_store, "/path/to/cache/directory"

缓存量会定期增长,需要自己定期清理旧缓存条目。

 

pasting

ActiveSupport::Cache::MemoryStore  这个是新建rails的默认配置

 


  rails5.2新功能

ActiveSupport::Cache::RedisCacheStore (点击看guide)

 

    Redis cache store利用Redis support当它达到最大memory时会自动驱逐(回收)eviction。行为类似于Memcached cache server。

     部署⚠️: Redis不会默认到期expire keys。留心使用专门的Redis cache server。不要用不稳定的缓存数据填满你的persistent-redis server!具体的见https://redis.io/topics/lru-cache

 

步骤:

1。gem 'redis'

配合使用gem 'hiredis'来加速hiredis连接库,Redis会自动请求/使用hiredis无需配置。

2. 加上配置config/environments/*.rb:

config.cache_store = :redis_cache_store, {url: ENV['REDIS_URL']} 

 

附加:生产模式下的配置样例:

 

cache_servers = %w(redis://cache-01:6379/0 redis://cache-02:6379/0)

 

config.cache_store = :redis_cache_store, { url: cache_servers,
 
  connect_timeout: 30,  # Defaults to 20 seconds
  read_timeout:    0.2, # Defaults to 1 second
  write_timeout:   0.2, # Defaults to 1 second
 
  error_handler: -> (method:, returning:, exception:) {
    # Report errors to Sentry as warnings
    Raven.capture_exception exception, level: 'warning',
      tags: { method: method, returning: returning }
  }
}

 

3个提示⚠️:

1. 设置cache read 和 write timeouts相对低一点,默认是1秒。可以设置更低

2. 默认设置是cache store不会再连接redis,如果在一次request后连接失败。

3. cache read和write不会raise exceptions;只会返回nil。为了判断出是否缓存出现意外,你可以自定义一个error_handler来报告你出现意外的信息。 包括开始的mehtod,returning和exception

 

 



 

Cache Keys

可以是任何对象。hash, array格式也行。 

 

 

 


 

 

对条件 GET 请求的支持

 

ActionController::ConditionalGet

 

stale?方法。expires_in方法, fresh_when方法。

  

get请求内传递了HTTP_IF_NONE_MATCH和HTTP_IF_MODIFIED_SINCE,服务器看这两个的值和服务器中的值是否一样。一样的话就证明上次返回的结果和本次请求的匹配。于是只返回一个空的响应,目的是节省资源。 

HTTP_IF_NONE_MATCH:内容标志符etag

HTTP_IF_MODIFIED_SINCE:updated_at时间戳。

如一样,则返回空 -- 条件get请求 


class ProductsController < ApplicationController 
  def show
    @product = Product.find(params[:id])
    # 如果根据指定的时间戳和 etag 值判断请求的内容不新鲜了
    # (即需要重新处理)执行这个块
    if stale?(last_modified: @product.updated_at.utc,  etag: @product.cache_key)
      respond_to do |wants|
        # ... 正常处理响应
      end
    end
    # 如果请求的内容还新鲜(即未修改),无需做任何事
    # render 默认使用前面 stale? 中的参数做检查,会自动发送 :not_modified 响应
    # 就这样,工作结束
  end
end 

简单写法fresh_when last_modified: @company.updated_at 

 

还可以传入模型,rails自动使用updated_at, cache_key设定last_modified和etag.

if stale?(@product)

 

也可以使用集合@products

fresh_when etag: @products 

 

使用http_cache_forever(public: true) 可以永久缓存页面,不会过期。

 

 

 

posted @ 2018-04-10 09:49  Mr-chen  阅读(198)  评论(0编辑  收藏  举报