访问计数 14742 (自2016年5月)
12?1442652655
发帖时间:09/12/2016 13:49
更新时间:09/12/2016 13:49

Ruby on Rails(RoR)是一个流行的开源 Web 框架。本文讲介绍5款帮助衡量 Rails 应用性能的工具。


1. Firebug
这是所有 Web 开发者必备的工具。

2. Rails Footnotes
这个工具可以显示应用的 footnotes,以便于调试,例如 sessions,请求参数,cookies,filter chain,routes,queries等等。

3. New Relic
这个工具可以让开发者迅速的分析最终用户的行为,追踪到服务器,甚至哪行代码,让性能管理变得无比容易。

4. Slim Scrooge
Slim Scrooge 是一个优化层,用于确保你的 Rails 应用只在需要的时候才从数据库获取内容,最大限度的降低网络流量,减少 SQL 执行以及和 Ruby 数据类型的转换过程。

5. Rails Analyzer
这个项目包含了一系列的 Rails 工具,可以让你捕捉应用中最慢的地方,让你的优化做的更有针对性。

回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:09/02/2016 19:30
更新时间:09/02/2016 19:32
最新的总结笔记迁至有道云,需要的联系
回复 ︿ (1)
  • 用户头像
    黄井泉 2年前
    有问题发至:huang.jingquan@163.com

0?1470885445
登录后可添加回复
12?1442652655
发帖时间:08/08/2016 10:11
更新时间:08/24/2016 15:43
大家可以从各个方面展开讨论
回复 ︿ (7)
  • 用户头像
    凌彬 2年前

  • 用户头像
    凌彬 2年前
    回个帖

  • 用户头像
    唐湘政 2年前

0?1470885445
登录后可添加回复
12?1442652655
发帖时间:05/10/2016 11:25
更新时间:05/10/2016 11:25

一.  依然利用任意一个model 的 find_by_sql() 来执行。

@result = Testmodels.findby('select  ....') .

结果可以进行 json 化. @result.to_josn


二. 利用ActiveRecord::Base.connection.execute 来执行,获取sql语句返回的metadata

@result = ActiveRecord::Base.connection.execute ‘select...."


三. 利用

 ActiveRecord::Base.connection.select_all ’sql_string。。。。‘ 的方法来获得metadata和结果集数组的 对象

回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:04/19/2016 15:48
更新时间:04/19/2016 15:48

總歸來說,依照Ajax使用的格式分類,有三種方式:

  • 向伺服器請求 HTML 片段,然後客戶端瀏覽器上的 JavaScript 再替換掉頁面上的元素
  • 向伺服器請求 JavaScript 程式腳本,然後客戶端瀏覽器執行它
  • 向伺服器請求 JSON 或 XML 資料格式,然後客戶端瀏覽器的 JavaScript 解析後再動作。

Ajax 表單

除了超連結 link_to 加上 :remote 可以變成 Ajax 之外,表單 form_for 也可以加上:remote變成 Ajax。

form_for @user, :remote => true

Ajax 按鈕

同理於超連結 link_to,按鈕 button_to 加上:remote => true參數也會變成 Ajax。

除了已經看過的:data => { :confirm => "Are you Sure?" }之外,disable_with可以避免使用者連續按下送出:

button_to "Remove", user_path(@user), :data => { :disable_with => 'Removing' }

第一種方式:替換 HTML 片段

編輯 app/views/events/index.html.erb 最下方加入:

<%= link_to 'Hello!', welcome_say_hello_path, :id => "ajax-load"  %>	<div id="content">
</div>

<script>
    $(document).ready(function() {
        $('#ajax-load').click( function(){
            $('#content').load( $(this).attr("href") );
            return false;
        });

    });
</script>

如此點下超連結後,就會把回傳的HTML置入到<div id="content">裡面。

第二種方式:使用 JavaScript 腳本

編輯 app/views/events/index.html.erb,在迴圈中間加入

<%= link_to 'ajax show', event_path(event), :remote => true %>

在迴圈外插入一個<div id="event_area"></div>

編輯 app/controllers/events_controller.rb,在 show action 中加入

respond_to do |format|
  format.html
  format.js
end

新增 app/views/events/_event.html.erb,內容與 show.html.erb 相同

新增 app/views/events/show.js.erb,內容如下

$('#event_area').html("<%= escape_javascript(render :partial => 'event') %>")
             .css({ backgroundColor: '#ffff99' });

瀏覽 http://localhost:3000/events

escape_javascript()可以縮寫為j()。

第三種方式:使用 JSON 資料格式

JavaScript Object Notation(JSON)是一種源自JavaScript的資料格式,是目前Web應用程式之間的準標準資料交換格式,在Rails之中,每個物件都有to_json方法可以很方便的轉換資料格式。

<%= link_to 'ajax show', event_path(event), :remote => true, :data => { :type => :json }, :class => "ajax_update" %>

點擊ajax show就會送出Ajax request了,但接下來要怎麼撰寫處理JSON的程式呢?

<script>
$(document).ready(function() {
    $('.ajax_update').on("ajax:success", function(event, data) {
        var event_area = $('#event_area');
        event_area.html( data.name );
    });
});
</script>

當然,你也可以把HTML片段當做JSON的資料來傳遞。

另一種JSON的變形是JSONP(JSON with Padding),將JSON資料包在一個JavaScript function裡,這個做的用處是讓這個API可以跨網域被呼叫。要回傳JSONP格式,只需要在render :json時多一個參數是:callback即可

respond_to do |format|
  format.json { render :json => @user.to_json, :callback => "process_user" }
end

事實上,Rails預設讓每個換頁都用上了Ajax技巧,這一招叫做Turbolinks,在預設的Gemfile中可以看到gem "turbolinks",以及Layout中的data-turbolinks-track。

它的作用是讓每一個超連結都只用Ajax的方式將整個body內容替換掉,這樣換頁時就不需要重新載入head部份的標籤,包括JavaScriptCSS等等,目的是可以改善換頁時的速度。

要特別注意因為它沒有整頁重新載入,所以如果有放在application.js裡面的$(document).ready程式就變成只有第一次載入頁面才執行到,換頁時就失效了。這時候必須改成$(document).on("page:change", function(){ ...})。

如果想要明顯體會它的效果,可以在app/assets/javascripts/application.js裡面加上

Turbolinks.enableProgressBar();

最後,因為它會影響JavaScriptEvent Bindings行為,所以在搭配一些JavaScript比較吃重的應用程式,例如使用JavaScript FrameworkBackboneAngularJSEmber時會移除不用,以免互相影響。

回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:03/24/2016 12:28
更新时间:03/24/2016 12:29

一、Preload

Preload 是以附加一条查询语句来加载关联数据的

1 User.preload(:posts).to_a 2 3 # => 4 SELECT "users".* FROM "users" 5 SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1)

 

这种方式与是 includes 默认加载数据的方式

由于 preload 总是生成两条 sql,所以不能在后面使用 where 条件,下面的查询会报错

User.preload(:posts).where("posts.desc=‘ruby is awesome‘") # => SQLite3::SQLException: no such column: posts.desc:
SELECT "users".* FROM "users" WHERE (posts.desc=ruby is awesome)

 

在 preload 的 where 条件只能这样使用

User.preload(:posts).where("users.name=‘Neeraj‘") # => SELECT "users".* FROM "users" WHERE (users.name=Neeraj)
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (3)

二、Includes

默认情况下 Includes 加载关联数据和 preload 一样

但它比 preload 要聪明一些。上面看到关于 preload 的查询 User.preload(:posts).where("posts.desc=‘ruby is awesome‘") 会失败。下面看看 includes:

User.includes(:posts).where(posts.desc = "ruby is awesome").to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE (posts.desc = "ruby is awesome")

 

这里你可以看到 includes 把两条分开的 SQL 用 LEFT OUTER JOIN 生成一条数据,并且它还包括了 where 条件

所以在一些情况下 includes 把两条语句合成一条。但最简单情况下它是使用两条的。但如果你就想要 includes 使用一条 sql 语句, references 可以做到:

User.includes(:posts).references(:posts).to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

 

上面只会生成一条 sql

 

三、Eager load

eager loading 是以 LEFT OUTER JOIN 加载所有相关数据的。

User.eager_load(:posts).to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

它正是 includes 使用 where 或 order 从 posts表中获取数据时强制生成一条 sql 的情况。

四、Joins

Joins 方式是使用 inner join 加载关联数据

User.joins(:posts) # => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"

 

上面的情况下 posts 数据不会被查询出来,它会得到重复数据。举个例子:

def self.setup
  User.delete_all
  Post.delete_all

  u = User.create name: Neeraj u.posts.create! title: ruby, desc: ruby is awesome u.posts.create! title: rails, desc: rails is awesome u.posts.create! title: JavaScript, desc: JavaScript is awesome u = User.create name: Neil u.posts.create! title: JavaScript, desc: Javascript is awesome u = User.create name: Trisha end

上面的测试数据运行之后会得到:

#<User id: 9, name: "Neeraj"> #<User id: 9, name: "Neeraj"> #<User id: 9, name: "Neeraj"> #<User id: 10, name: "Neil">

 

我们可以使用 distinct 来去重:

User.joins(:posts).select(distinct users.*).to_a

 

如果想要 posts 表的数据也可以 select 出来

records = User.joins(:posts).select(distinct users.*, posts.title as posts_title).to_a
records.each do |user| puts user.name
  puts user.posts_title
end

 

注意:在应用 joins 查询数据的时候使用 user.posts 会生成另外一个 sql 语句

回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:03/21/2016 14:22
更新时间:03/21/2016 14:44

比如有一个users表,要得到user的id数组:

select id from users where age > 20;

要实现在如上sql语句,在rails中有以下几种写法:

  1. User.where(‘age > 20‘).select(:id).collect(&:id)
  2. User.where(‘age > 20‘).select(:id).map(&:id)  -> SELECT id FROM `users` WHERE (age < 20)
  3. User.where(‘age > 20‘).pluck(:id)              -> SELECT `users`.`id` FROM `users` WHERE (age < 20)

前两种方法是一样的,都是从数据库中查出数据,然后进行循环,从active record的数据集取出id组成数组。

而pluck方法则是只查id字段,直接返回id的数组,而不是返回active record的数据集,这样我们就不需要再用循环取出id数组。

通常、pluck方法的效率要比上面那两种办法高。

在rails4.0以上,pluck可以传多个字段作为参数。

Person.pluck(:id, :name) -> [[1, ‘David‘], [2, ‘Jeremy‘], [3, ‘Jose‘]]

回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:03/21/2016 14:36
更新时间:03/21/2016 14:37

N+1问题之includes应用


# model
class User < ActieRecord::Base
  has_one :car
end

class Car < ActiveRecord::Base
  belongs_to :user
end

# your controller
def index
  @users = User.page(params[:page])
end

# view
<% @users.each do |user| %>
 <%= user.car.name %>
<% end %>

我们在view中读取的user.car.name的值。但是这样的程式就导致了N+1温蒂,假设User有10笔,这样的程式会执行11数据库查询,一条是查出User,另外十条是一条一条去查car,严重拖慢性能。

SELECT * FROM `users` LIMIT 10 OFFSET 0
SELECT * FROM `cars` WHERE (`cars`.`user_id` = 1)
SELECT * FROM `cars` WHERE (`cars`.`user_id` = 2)
SELECT * FROM `cars` WHERE (`cars`.`user_id` = 3)
...
...
...
SELECT * FROM `cars` WHERE (`cars`.`user_id` = 10)

解決方法,加上includes:

# your controller
def index
  @users = User.includes(:car).page(params[:page])
end

如此SQL query就只有两个,只用一个就捞出了所有Cars资料

SELECT * FROM `users` LIMIT 10 OFFSET 0
SELECT * FROM `cars` WHERE (`cars`.`user_id` IN('1','2','3','4','5','6','7','8','9','10'))

如果user还有其它关联资料想要一起捞出来,includes也支援hash写法::@users = User.includes(:car => :parts ).page(params[:page])


回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:02/25/2014 08:39
更新时间:02/25/2014 08:39
本人现抛砖引玉,贴一段代码。
本人现抛砖引玉,贴一段代码。
本人现抛砖引玉,贴一段代码。
回复 ︿
0?1470885445
登录后可添加回复
12?1442652655
发帖时间:11/29/2013 09:31
更新时间:01/23/2014 08:56
创业的故事
回复 ︿ (2)
  • 用户头像
    谭显波 5年前
    测试

  • 用户头像
    尹刚 5年前
    怎么没有内容?

0?1470885445
登录后可添加回复
点击展开更多