Rails Concepts Series:
https://gorails.com/series/rails-concepts
- 基本都是免费的
- 一些细小的知识点,很有帮助。
- URL和parameter 的简单解析
- 做一个app template
- 使用 Ruby on Rails 的❌页面debug
- rails5的actioncable和websockets介绍(有多个系列的cable视频)
- Form的submit方法解释:params[:commit] == value
- 记录关联中的选项:counter_cache
URL和parameter 的简单解析。
http://localhost:3000/products?test=111
在routes.rb中
get '/products', to: "products#indx"
Rails routes会解析url,提取一个参数: params[:test] = 111
这个参数传给controller中的 Product#index:
@test = params[:test]
在views/products/index.html.erb写:
<%= @test%>
最后会在web page 上显示。
http://localhost:3000/products/dd?test=111
在routes.rb中
get '/products', to: "products#index"
get '/products/id', to: 'products#show'
Rails routes会解析url,提取2个参数: id和test
这个2参数传给controller中的 Product#show:
@test = params[:test]
@id = params[:id]
在views/products/index.html.erb写:
<%= @test%> <%= @id%>
最后会在web page 上显示这2个参数
在termiinal的log上,会出现:Parameters: {"test" => "111", "id" => "dd"}
做一个app template
Rails指导:
http://guides.rubyonrails.org/generators.html#application-templates
(一点感悟,光看指导,看不明白,理解不清楚。还是视频比较好,有step to step。方便理解)
视频教程:
https://gorails.com/episodes/rails-application-templates?autoplay=1
教程案例:本地:/自我练习/jumpstart-master/whatever
⚠️,不难理解,但是有很多兼容性等问题,需要测试模版是否好使。所以使用现成的模版稍微修改比较好。
基础:
使用-m参数建立一个预制自定义gem的Rails app。
rails new myapp -m template.rb
下载git上的zip压缩包, Download ZIP, 解压缩后在,那个文件夹运行上面的命令。
也可以使用线上模版如:
rails new myapp -m https://raw.github.com/80percent/rails-template/master/composer.rb
打开模版设置的文件的命令是:atom template.rb
文档的理解:
先设定方法:
- 定义add_template_repository_to_source_path
- 定义add_gems,即所要增加的gem
- 定义set_application_name
- 每个gem各自的设置,都需要定义。比如devise,需要def add_users, 挺复杂的,需要根据各自的gem使用文档来设置。
- 其他一些相关设置,比如打开spring。
- 执行上面的方法。在add_gems后,使用after_bundle(&block), 结束。
如何使用 Ruby on Rails 的❌页面debug
弹出红色页面:
首先要看标题,和标题下的解释。页面最下方有控制台,可以进行变量的输入测试反馈。
如果不能找到问题。则需要看下面的一大堆stack tracks。
例子
actionpack(3.2.8) lib/action_controller/metal/data_streaming.rb:125:in 'send_file_headers!'
。。。略。。一堆
app/controllers/charges_chntroller.rb:13:in 'block (2 levels) in show'
...
第一行actionpack(3.2.8) ...,指actionpack的版本,及出错的文件位置。这是源代码库的代码。往下看,找到自己写的代码的文件app/controllers/..., 然后回到atom查看细节。
如果你怀疑这是一个方法使用的错误,看看是否用错参数及其格式。可以去API上查询此方法的详细信息。
还是找不到问题的话,并猜测是否是actionpack版本过期了?
可以去git上rails/rails, 找对应的版本分支,根据文件路径打开对应的文件,并定位。
右上角有一个blame按钮,可以查看这行代码之前的变更记录。
总之,报错页的的标题,是可以定位bug,比如通过复制粘到google上查询类似问题,比较注重速度和效率。但查stack track才能够最准确的定位问题。这叫做读你的代码的故事。
如果询问别人问题,把stack track粘贴上,很重要。
第一步,看错误标题及相关。这通常可以有效率解决问题,如果不能看第二步⬇️。
第二步,通过stack track看最后列出的你写的code。 并确认是否是正确的。如果还不能解决问题。见第三步⬇️。
第三步, 查看源代码库,通过第一行的版本和路径查看。
什么是webstocket和actioncable
之前学习的博客:http://www.cnblogs.com/chentianwei/p/8690304.html
这章节过于基础,我需要一个完整的,最好是实际的产品的教学。看下面系列2.
有2个系列:
- ActionCable 介绍(感觉比较深的4个方面的知识块)
- Realtime Group Chat with ActionCable (7节课,用Rails5建立一个类似的Slack交流组)
Rails Forms with Multiple Submit Buttons
讲解ActionView::Helpers::Formbuilder#submit(value =nil, options={})
还有一个submit_tag(value = "提交", options={})
submit方法会根据对象是否是new而在controller中选择是create还是update。
另外,开发者可以根据value的值,定义不同的额外行为。例子见下:
⚠️解析成HTML,input标签内有name, value,2个特性:
submit_tag "Complete sale", data: { disable_with: "Submitting..." }
# => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
这2个特性组成了,在点击提交按钮后,request中的参数之一: {..., "commit" => "提交"}
因此,params[:commit] 的值是"提交"。 根据value的不同,可以在同一个表格使用多个按钮, 并在controller中,设定针对不同value的不同方法。
比如,在创建或者更新的web页面,在“发布”按钮旁边增加一个“不发布“按钮。这两个按钮都会创建或更新一个记录,但会根据value,进行后续的行为,这里可以在index的页面隐藏“不发布”的记录。
button,button_tag方法和submit方法的相似和区别:
- 都可以作为提交按钮,默认设置 type="submit"
- button可以接受一个块参数。type特性可以设置其他类型,如"reset" 或者javascript。
Active Record Associations中的:counter_cache
前置知识:
Controlling caching:
第一次从关联对象来查询数据,是从数据库中查询,查询结果会存储在缓存中。方便之后直接从缓存查询,速度提高。
但有时候数据库更新太快。因此更新缓存, 使用reload方法。
- author.books #从数据库取查询记录books
- author.books.size #从cache中得到
- author.books.reload.empty? #丢弃cache,从数据库取数据。
Controlling Association Scope
默认的关联对象之间是处于同一个模型module的scope范围。但例外是:如果两个对象定义在不同的scopes中,关联会失效,这是必须指定class_name ,明确说明属于哪个模型的类。
案例见:http://guides.rubyonrails.org/association_basics.html#tips-tricks-and-warnings
另外,也可以用于自定义一个关联的对象名字,然后用class_name说明关联的真正类。
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account ,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier ,
class_name: "MyApplication::Business::Supplier"
end
end
end |
Bi-directional Associations 双向关联
默认简单的两个模型的关联,当一条记录的属性被改变,但没储存到数据库,关联记录是可以从缓存中得到这个改变的属性的。
a = Author.first
b = a.books.first
a.first_name == b.author.first_name 返回true
a.first_name = "David" #没有保存到数据库,只是保存到缓存了。
a.first_name == b.author.first_name 从缓存中进行比较,返回true
但是如果关联包含scope或者如:through, :foreign_key 等选项,
即间接的关联 ,则
a.first_name == b.author.first_name 返回false
可以使用inverse_of选项,来建立bi-directional 关联:
如:
class Author < ApplicationRecord
has_many :books, inverse_of: "writer"
end
class Book < ApplicationRecord
belongs_to :writer, class_name: "Author", foreign_key: "author_id"
end
counter_cache
查询@author.books.size的值,通过执行数据库语法COUNT(*) query.
但无需每次都调用数据库。比如用在微博主页,每个大vip有多少粉丝。 对非时效性和准确性的数据,可以降准提速。
在belongs_to上加上选项counter_cache:true,Rails会把缓存的value更新到数据库,然后返回这个值来响应size方法。
⚠️, 在Author上需要有一个column books_count
(类名_count)
我的理解:第一次调用@author.books.size,会使用数据库计算,并存入books_count,下次再用就从books_count中调用数据了。
另外可以使用counter_cache: :自定义名字,来替代true,然后在has_many的表中定义这个名字作为列名。
class Book < ApplicationRecord
belongs_to :author, counter_cache: true
end class Author < ApplicationRecord
has_many :books
end |
这样,如果增加了一条Book记录,会自动更新Author中对应的author.books_count。
同样,如何删除一条Book记录,也会自步更新author.books_count的数量,-1.
ActiveRecord::CounterCache::ClassMethods
有一个reset_counters方法, 一般用于重置。
比如以前没有使用counter_cache,现在已经有了一堆相关的记录。可以:
rails g migration addXXX,然后在db/migrate/XXX_add_xxx中添加:
add_column :users, :forum_posts_count, :integer, default: 0, null: false
User.find_each {|u| User.reset_counters(u.id, :forum_posts)}
数据量非常大的时候,加上 batch_size: 1000
find_each(batch_size: 1000) do ...end
或者使用原始SQL语法:
execute <<-SQL.squish
update users set forum_posts_count = (
select count(1) from forum_posts where forum_posts.user_id = users.id
)