日志做分割是必须的,不然服务一直跑下去,某一天你会发现磁盘满了!下面就介绍下 logrotate 做日志分割的过程。
首先得安装 logrotate 。
1
|
|
修改 logrotate 的配置文件。
1 2 3 4 5 6 7 8 9 10 11 |
|
这样的话每天都会自动做日志分割,当然也可以强制执行。
1
|
|
想了解 logrotate 的配置文件该怎么写,可以看 http://linuxers.org/howto/howto-use-logrotate-manage-log-files。
日志做分割是必须的,不然服务一直跑下去,某一天你会发现磁盘满了!下面就介绍下 logrotate 做日志分割的过程。
首先得安装 logrotate 。
1
|
|
修改 logrotate 的配置文件。
1 2 3 4 5 6 7 8 9 10 11 |
|
这样的话每天都会自动做日志分割,当然也可以强制执行。
1
|
|
想了解 logrotate 的配置文件该怎么写,可以看 http://linuxers.org/howto/howto-use-logrotate-manage-log-files。
最近开始看 Rails 4 的源码,打算写一系列的 Rails 4 源码阅读的文章,这是第一篇.
我是因为想知道 Rails 4 里面的 timestamps 是在什么时候赋值或者更新的,然后我翻看了下 Rails 4 的 ActiveRecord 代码.
在https://github.com/rails/rails/blob/master/activerecord/lib/active_record/timestamp.rb里面有create_record和update_record方法,更新这些timestamps
的操作就在这个里面了.
恩,应该是这样.update_record
与create_record
分别是创建和更新必掉的方法,所以如果有 timestamps 并且开启了record_timestamps
就会去赋值或者更新这些字段了.
其实也可以从save
开始阅读,save
的过程可以简化成这样.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
|
哇,就是这样的顺序,但是感觉有点复杂啊,save
到处都是,通过super
关键字逐层调用,就像一个rack stack
一样!最主要的是我们要知道save
的调用链是怎么样的,这个可以看ActiveRecord::Base.ancestors
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
这样就能知道调用链的情况了.当然还可以去debugger
.
恩,save
就是这样!timestamps
又明白了,太棒了~!
当使用元编程动态生成代码时,可能会有一些不符合命名规范的名称,那么就可以用到下面的技巧,姑且称之为safe name
.
以下出自 Rails 源码 define_method_attribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
safe name
技巧的原理就是构造符合命名规范的名称(通过一些算法,像unpack,hash都可以),
像这里的话是通过unpack('h*')
生成一个字串safe_name
.
这个技巧真心很赞!!!
测试的时候,有个方法里面调用了Resque.enqueue
,我觉得不好测试.
可能有人会说只需要测试是否调用了Resque.enqueue
,或者只需要测试redis
里面是否有这个队列.
但是我觉得最好还是要测试到后台任务的执行结果,因为后台任务的最终结果可能就是这个方法的关键之处,是这个方法的主体.所以最好还是能去测试后台任务的结果(当然,视代价而定)!~
好了,回到正题.怎么去测试Resque
任务的执行结果呢?额,开着后台worker
?然后在测试代码里面sleep 10
等待后台执行?我擦,太二了!
# If 'inline' is true Resque will call #perform method inline # without queuing it into Redis and without any Resque callbacks. # The 'inline' is false Resque jobs will be put in queue regularly.
大意就是
如果 inline 为 true, Resque 任务会直接执行不需要放到 redis 队列中,并且不执行任何 callbacks。 inline 是 false, Resque 任务将放在 redis 队列里面等待执行。
恩,nice!这个就是我想要的,同步执行!
首先要看压队列的方法 Resque#enqueue.
1 2 3 |
|
这个方法是将job
压入redis
队列.再看看 queue_from_class方法.
1 2 3 4 5 6 7 |
|
这个方法是用来获取job
对应的队列名的.
再回到enqueue
方法,看看 enqueue_to 方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
这个方法是去验证job
,并且执行hooks
,创建job
.
先看看 validate.
1 2 3 4 5 6 7 8 9 10 11 |
|
这个方法是去校验是否有队列名以及job class
.
再看看 Plugin.before_enqueue_hooks.
1 2 3 4 5 |
|
这个方法是获取before_enqueue hook
的列表.然后
1 2 3 |
|
这段代码会去执行所有的before_enqueue hook
,并且校验所有before_enqueue hook
执行结果,如果有为false
,则直接return
,否则创建job
,并且执行所有的after_enqueue hook
.
我们再仔细看看 Job.create.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
关键的地方来了!!!
额,先稍等,一步一步来.
1 2 3 4 5 6 |
|
不难看出,是用来解析队列参数args
的,这里默认是 JsonCoder,
用JSON
来解析参数.
前文已经讲过,用来校验队列名以及job class
.
如果非inline
,将job
压入队列,等待异步执行.
如果inline
,直接执行job
.可以具体看看是怎么执行的.
先获取job
参数,再用参数new
一个job
出来,然后执行perform
方法.关键就在perform
方法,这个方法就是去真正执行job
了.
下面再来具体看看到底是怎么perform
的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
|
由上述代码可以看出,其实真正执行的方法还是job class
的perform
方法,并且会去执行诸如before_perform
around_perform
after_perform
等为前缀的job class
方法.
整个Resque
同步执行的过程就是这样!
看完代码之后,我直接提了个issue,
不知道是不是没开发完还是怎么回事!inline
的描述和真实的行为竟然不一样!!!inline
为true
,还是会去执行callbacks
!
看了这篇文章后,脑子里应该能有一个大概的流程图,知道Resque
大概的执行过程,这样我的目标就算达到了.后面我会再写一篇Resque 异步执行的文章,其实都是一样的,这个留到下次文章再分析.
当我遇到不明白的事物的时候,我会先看表象,了解到表象后,再深入了解其内部。不能说这种方法好,但是对我来说还是挺有效的(年轻的coder)。当然,最好还是直接深入内部去了解,这样更省事省时。
到正题,let 与 let的说明如下:
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time
the method it defines is invoked. You can use let! to force the method's
invocation before each example.
其实 let
let!
以及 subject
都是 memoized_helpers,都是通过委托来定义一个消息的接收方!好处就是可以让代码更精炼,提高可读性,减少重复。
要比较三者间的区别,必须先聊聊let
。
1 2 3 4 5 6 7 8 9 10 11 |
|
首先定义一个MemoizedHelpers
,就是memoize method
,只要调用过,就会被缓存起来,下次调用还是返回上次相同的对象。
验证起来也很简单,可以看看两次调用结果的 object_id
。
1 2 3 4 5 6 7 8 |
|
输出结果: 70186054302440 70186054302440
首先上源码吧! let 源码上面已经贴了, 接下来看看 let! subject subject!的源码。
1 2 3 4 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
可以看得出,let!
其实也是调用let
,并且在before each
调用,区别就是let!
会在before each
调用,而let
不会,区别仅此而已!
subject
就稍微的复杂了一点,首先如果subject
指定了 name ,则会先用let
生成一个叫 name 的 memoize method
,然后再次用let
生成一个叫 subject 的memoize method
。这样当调用 subject 的时候,其实最终调用的是 name 这个memoize method
。当subject
没有指定 name 的时候,则生成用let
直接生成一个名叫 subject 的memoize method
。额,是不是有点乱?其实细细体味一下这个方法,哇塞,真的很棒,还这么精炼!用到了递归,哈哈!但是我又有点奇怪,为什么要多此一聚呢,而不直接用let
。恩,看源码。
1 2 3 |
|
原来,subject
是用来配合 should
进行隐式调用的,在这里何为隐式调用?看源码里面的例子:
1 2 3 4 5 |
|
恩,rspec
设计的就是这么巧妙!
这个就不需要多说了,也就是比subject
多了句before each
。
1 2 3 4 |
|
其实什么时候用let
subject
,这个可以根据字面意思来,如果是测试主题,并且需要多次调用,就可有写成一个subject
。而非测试主题,又需要多次调用,则可以用let
!
本文档翻译自 Arnau Sanchez (tokland)所编译的这份文档 RubyFunctionalProgramming。
同时也有日文版本。
命令式编程比较牛吗? 不!不!不!只是比较快,比较简单,比较诱人而已。
x = x + 1
在以前上小学的美好回忆裡,我们可能都曾对上面这行感到疑惑。这个 x
到底是什么呢?为什么加了一之后,x
仍然还是 x
。
不知道为什么,我们就开始写程序了,也就不在乎这是为什么了。心想:“嗯”,“这不是什么大问题,编程就是事情做完最重要,没有必要去挑剔数学的纯粹性 (让大学裡的大鬍子教兽们去烦恼就好)” 。但我们错了,也因此付出极高的代价,只因我们不了解它。
维基百科的解释:“函数式编程是一种写程序的范式,将计算视为对数学函数的求值,并避免使用状态及可变的数据” 换句话说,函数式编程提倡没有副作用的代码,不改变变量的值。这与命令式编程相反,命令式编程强调改变状态。
令人惊讶的是,函数式编程就这样而已。那…有什么好处呢?
更简洁的代码:“变量”一旦定义之后就不再改动,所以我们不需要追踪变量的状态,就可以理解一个函数、方法、类别、甚至是整个项目是怎么工作的。
引用透明:表达式可以用本身的值换掉。如果我们用同样的参数调用一个函数,我们确信输出会是一样的结果(没有其它的状态可改变它的值)。这也是为什么爱因斯坦说:“重复做一样的事却期望不同的结果”是疯狂的理由。
引用透明打开了前往某些美妙事物的大门
并行化:如果调用函数是各自独立的,则他们可以在不同的进程甚至是机器裡执行,而不会有竞态条件的问题。“平常” 写并发程序讨厌的细节(锁、semaphore…等)在函数式编程裡面通通消失不见了。
记忆化:由于函数调用的结果等于它的返回值,我们可以把这些值缓存起来。
模组化:代码裡不存有状态,所以我们可以将项目用小的黑箱连结起来,函数式编程提倡自底向上的编程风格。
容易调试:函数彼此互相隔离,只依赖输入与输出,所以很容易调试。
一切都是这么美好,但怎样才能将函数式编程,应用到每天写 Ruby(Ruby 不是个函数式语言)的程序开发裡呢?函数式编程广义来说,是一种风格,可以用在任何语言。当然啦,用在特别为这种范式打造的语言裡显得更自然,但某种程度上来说,可以应用到任何语言。
让我们先釐清这一点:本文没有要提倡古怪的风格,比如仅仅为了要延续理论函数式编程的纯粹性所带来的古怪风格。反之,我想说的重点是,我们应该 当可以提昇代码的品质的时候,才使用函数式编程 ,不然这只不过是个糟糕的解决办法。
别更新它们,创造新的变量。
append
No:
indexes = [1, 2, 3]
indexes << 4
indexes # [1, 2, 3, 4]
Yes:
indexes = [1, 2, 3]
all_indexes = indexes + [4] # [1, 2, 3, 4]
No:
hash = {:a => 1, :b => 2}
hash[:c] = 3
hash
Yes:
hash = {:a => 1, :b => 2}
new_hash = hash.merge(:c => 3)
No:
string = "hello"
string.gsub!(/l/, 'z')
string # "hezzo"
Yes:
string = "hello"
new_string = string.gsub(/l/, 'z') # "hezzo"
No:
output = []
output << 1
output << 2 if i_have_to_add_two
output << 3
Yes:
output = [1, (2 if i_have_to_add_two), 3].compact
如果一个语言要搞函数式,会需要高阶函数。高阶函数是什么?函数可以接受别的函数作为参数,并可以返回函数,就这么简单。
Ruby (与 Smalltalk 还有其它语言)在这个方面上非常特别,语言本身就内置这个功能: blocks 区块。区块是一段匿名的代码,你可以随意的传来传去或是执行它。让我们看区块的典型用途,来构造函数式编程的构造子。
No:
```Ruby
dogs = []
["milu", "rantanplan"].each do |name|
dogs << name.upcase
end
dogs # => ["MILU", "RANTANPLAN"]
```
Yes:
```Ruby
dogs = ["milu", "rantanplan"].map do |name|
name.upcase
end # => ["MILU", "RANTANPLAN"]
```
No:
dogs = []
["milu", "rantanplan"].each do |name|
if name.size == 4
dogs << name
end
end
dogs # => ["milu"]
Yes:
dogs = ["milu", "rantanplan"].select do |name|
name.size == 4
end # => ["milu"]
No:
length = 0
["milu", "rantanplan"].each do |dog_name|
length += dog_name.length
end
length # => 15
Yes:
length = ["milu", "rantanplan"].inject(0) do |accumulator, dog_name|
accumulator + dog_name.length
end # => 15
在这个特殊情况下,当累积器与元素之间有操作进行时,我们不需要区块,只要将操作传给符号即可。
length = ["milu", "rantanplan"].map(&:length).inject(0, :+) # 15
想像一下,你不仅想要摺迭(fold)的结果,也想要过程中产生的部分数值。用命令式编程风格,你可能会这么写:
lengths = []
total_length = 0
["milu", "rantanplan"].each do |dog_name|
lengths << total_length
total_length += dog_name.length
end
lengths # [0, 4, 15]
在函数式的世界裡,Haskell 称之为 scan, C++ 称之为 partial_sum, Clojure 称之为 reductions。
令人讶异的是,Ruby 居然没有这样的函数!让我们自己写一个。这个怎么样:
lengths = ["milu", "rantanplan"].partial_inject(0) do |dog_name|
dog_name.length
end # [0, 4, 15]
Enumerable#partial_inject 可以这么实现:
module Enumerable
def partial_inject(initial_value, &block)
self.inject([initial_value, [initial_value]]) do |(accumulated, output), element|
new_value = yield(accumulated, element)
[new_value, output << new_value]
end[1]
end
end
实作的细节不重要,重要的是,当认出一个有趣的模式可以被抽象化时,我们将其写在另一个函式库,撰写文档,反覆测试。现在只要让实际的需求去完善你的扩充即可。
这样的程序我们常常看到:
name = obj1.name
name = obj2.name if !name
name = ask_name if !name
在此时你应该觉得这样的代码使你很不自在(一个变量一下是这个值,一下是这个;变量名 name
到处都是…等)。函数式的方式更简短,也更简洁:
name = obj1.name || obj2.name || ask_name
另一个有更复杂条件的例子:
def get_best_object(obj1, obj2, obj3)
return obj1 if obj1.price < 20
return obj2 if obj2.quality > 3
obj3
end
可以写成像是这样的一个表达式:
def get_best_object(obj1, obj2, obj3)
if obj1.price < 20
obj1
elsif obj2.quality > 3
obj2
else
obj3
end
end
确实有一点囉嗦,但逻辑比一堆行内 if/unless
来得清楚。经验法则告诉我们,仅在你确定会用到副作用时,使用行内条件式,而不是在变量赋值或返回的场合使用:
country = Country.find(1)
country.invade if country.has_oil?
# more code here
Vanilla Ruby 没有从 Enumerable 转到 Hash 的直接对应(本人认为是一个遗憾的缺陷)。这也是为什么新手持续写出下面这个糟糕的模式(而你怎么能责怪他们呢?唉!):
```Ruby
hash = {}
input.each do |item|
hash[item] = process(item)
end
hash
```
这真的非常可怕!阿~~~!但手边有没有更好的办法呢?过去 Hash 构造子需要一个有着连续键值对的 flatten 集合 (阿,用 flatten 数组来描述映射?Lisp 曾这么做,但还是很丑陋)。幸运的是,Ruby 的最新版本也接受键值对,这样更有意义(作为 `hash.to_a` 的逆操作),现在你可以这么写:
```Ruby
Hash[input.map do |item|
[item, process(item)]
end]
```
不赖嘛,但这打破了平常的撰写顺序。在 Ruby 我们期望从左向右写,给对象调用方法。而“好的”函数式方式是使用 `inject`:
```Ruby
input.inject({}) do |hash, item| hash.merge(item => process(item)) end “`
我们都同意这还是很囉嗦,所以我们最好将它放在 Enumerable 模组,Facets 正是这么干的。它称之为 Enumerable#mash:
module Enumerable
def mash(&block)
self.inject({}) do |output, item|
key, value = block_given? ? yield(item) : item
output.merge(key => value)
end
end
end
["functional", "programming", "rules"].map { |s| [s, s.length] }.mash
# {"rules"=>5, "programming"=>11, "functional"=>10}
或使用 mash
及 选择性区块来一步完成:
["functional", "programming", "rules"].mash { |s| [s, s.length] }
# {"rules"=>5, "programming"=>11, "functional"=>10}
Joe Armstrong (Erlang 发明人) 在 “Coders At work” 谈论过面向对象编程的重用性:
“我认为缺少重用性是面向对象语言造成的,而不是函数式语言。面向对象语言的问题是,它们带着语言执行环境的所有隐含资讯四处乱窜。你想要的是香蕉,但看到的却是香蕉拿在大猩猩手裡,而大猩猩的后面是整个丛林”
公平点说,我的看法是这不是面向对象编程的本质问题。你可以写出函数式的面向对象程序,但确定的是:
Rich Hickey,Clojure 的发明人(一个给 JVM 用的函数式 Lisp 方言),在这场出色的演讲裡谈论了状态、数值以及同一性。
可以这么写:
if found_dog == our_dog
name = found_dog.name
message = "We found our dog #{name}!"
else
message = "No luck"
end
然而,控制结构(if
, while
, case
等)也返回表达式,所以只要这样写就好:
message = if found_dog == my_dog
name = found_dog.name
"We found our dog #{name}!"
else
"No luck"
end
这样子我们不用重复变量名 message
,企图也更明显:当有段长的程序(用了一堆我们不在乎的变量),我们可以专注在程序在干什么(返回讯息)。再强调一次,我们在缩小程序的作用域。
另一个函数式程序的好处是,表达式可以用来构造数据:
“`Ruby { :name => “M.Cassatt”, :paintings => paintings.select { |p| p.author == “M.Cassatt” }, :birth => painters.detect { |p| p.name == “M.Cassatt” }.birth.year, … }
1 2 3 4 5 6 7 8 |
|
Ruby RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false, }
1 2 |
|
Ruby module Math def self.factorial_tco(n, acc=1) n < 1 ? acc : factorial_tco(n-1, n*acc) end end “`
在递归深度不太可能很深的情况下,你仍可以使用:
class Node
has_many :children, :class_name => "Node"
def all_children
self.children.flat_map do |child|
[child] + child.all_children
end
end
end
```
### 惰性枚举器
惰性求值延迟了表达式的求值,在真正需要时才会求值。与 eager evaluation 相反,eager evaluation 当一个变量被赋值时、函数被调用时…甚至根本没用到变量等状况,都立马对表达式求值,惰性不是函数式编程的必需品,但这是个符合函数式范式的好策略(Haskell 大概是最佳的例子,瀰漫着懒惰的语言)。
Ruby 所採用的基本上是 eager evaluation(虽然许多其它的语言,在条件还没满足前不对表达式求值,以及短路布林运算 `&&`, `||` 等)。然而,与任何内置高阶函数的语言一样,延迟求值是隐性支援,因为程序员自己决定区块何时被调用。
Enumerators 同样 从 Ruby 1.9 开始支援(1.8 请用 backports),它们提供了一个简单的介面来定义惰性 enumerables。经典的例子是构造一个枚举器,返回所有的自然数:
```Ruby
require 'backports' # 1.8 才需要
natural_numbers = Enumerator.new do |yielder|
number = 1
loop do
yielder.yield number
number += 1
end
end
```
可以用更函数式的精神改写:
```Ruby
natural_numbers = Enumerator.new do |yielder|
(1..1.0/0).each do |number|
yielder.yield number
end
end
```
natural_numbers.take(10)
现在,试试给 `natural_numbers` 做 `map`,发生什么事?它不会停止。标准的 enumerable 方法 (`map`, `select` 等)返回一个数组,所以在输入流是无穷大时,无法正常工作。让我们扩展 Enumerator 类别,比如加入这个惰性的 Enumerator#map:
```Ruby
class Enumerator
def map(&block)
Enumerator.new do |yielder|
self.each do |value|
yielder.yield(block.call(value))
end
end
end
end
```
现在我们可以给所有自然数的流做 `map` 了:
```Ruby
natural_numbers.map { |x| 2*x }.take(10)
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
```
枚举器是用来构造惰性行为的区块的好东西,但你可以使用用懒惰风格,实作了所有 enumerable 方法的函式库:
https://github.com/yhara/enumerable-lazy
require ‘enumerable/lazy’ (1..1.0/0).lazy.map { |x| 2*x }.take(10).to_a
#### 惰性求值的好处
1. 显而易见的好处: 无需在不必要的情况下,构造、储存完整的结构(也许,可以更有效率的使用 CPU 及内存)
2. 不太显而易见的好处: 惰性求值使写程序不需要了解超出你所需的范围。让我们看一个例子:你写了某种解题工具,可以提供无数种解法,但在某个时候,你只想要前十种解法。你可能会这么写:
solver(input, :max => 10) “`
当你与惰性结构一起工作时,不需要说什么时候该结束。调用者自己会决定他需要多少值。代码变得更简单,责任归属到对的地方,也就是调用者:
solver(input).take(10)
练习:“前十个平方可被五整除的自然数的和是多少?”
Integer::natural.select { |x| x**2 % 5 == 0 }.take(10).inject(:+) #=> 275
让我们跟等价的命令式版本来比较:
n, num_elements, sum = 1, 0, 0
while num_elements < 10
if n**2 % 5 == 0
sum += n
num_elements += 1
end
n += 1
end
sum #=> 275
我希望这个例子展示了这个文档裡讨论的函数式编程的优点:
更简洁: 你会撰写更少的代码。函数式程序处理的是表达式,而表达式可以连锁起来;命令式程序处理的是变量的改动(叙述式),而这不能连锁。
更抽象: 你可以争论我们使用 select
, inject
…等等,来隐藏了一大堆代码,我很高兴你这么说,因为我们正是这么干的。将通用的、可重用的代码隐藏起来,这是所有编程的重点 –– 但函数式编程特别是关于如何撰写抽象。感到开心不是因为写了更少的代码,而是因为藉由认出可重用的模式,简化了代码的复杂性。
更有声明式的味道: 看看命令式的版本,第一眼看起来是一沱无用的代码 –– 没有注解的话 –– 它会做什么你完全没有概念。你可能会说:“好吧,从这裡开始读,草草记下 n
与 sum
的值,进入某个迴圈,看看 n
与 sum
的值如何变化,看看最后一次迭代的情形” 等等。函数式版本另一方面是自我解释的,函数式版本描述、声明它在干的事,而不是如何干这件事。
“函数式编程就像是将你的问题叙述给数学家一样。命令式编程像是给白痴下指令” (arcus 在 Freenode #scheme 频道所说)
更好的理解函数式编程的原理,帮助我们写出更清晰、重用性更高并更简洁的代码。Ruby 基本上是一个命令式语言,但它也有很大的函数式能力,明白什么时候用,及如何用(以及何时不该用)这些能力。将这句话当成你的座右铭吧 “状态是万恶的根源,尽可能避免它。”
Workshop at [Conferencia Rails 2011](http://conferenciarails.org/): [Functional Programming with Ruby](http://public.arnau-sanchez.com/ruby-functional/) [(slideshare)](http://www.slideshare.net/tokland/functional-programming-with-ruby-9975242)
http://en.wikipedia.org/wiki/Functional_programming
http://www.defmacro.org/ramblings/fp.html 译文
http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html
http://www.khelll.com/blog/ruby/ruby-and-functional-programming/
http://www.bestechvideos.com/2008/11/30/rubyconf-2008-better-ruby-through-functional-programming
http://channel9.msdn.com/Blogs/pdc2008/TL11
http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey
This document is licensed under the CC-By 3.0 License, which encourages you to share these documents. See http://creativecommons.org/licenses/by/3.0/ for more details.
最近项目用的 Resque 老是会有一些莫名其妙的问题,非常头疼!
1
|
|
实在是没办法了,然后仔细的去阅读了下 Resque 的 wiki,有种恍然大悟的感觉。
原来我遇到的问题大家都遇到过,并且给出了解决方案,就拿 Resque 数据库链接错误来说,Resque 原作者已经有了推荐的解决方案,原文如下:
If your workers remain idle for too long they may lose their MySQL connection.
If that happens we recommend using this Gist.
然后我在项目 config/initializers
目录中的 resque.rb
文件中加入代码:
1 2 3 |
|
问题就这样解决了!但是,引入一个 Gem
,我连文档都没有仔细阅读,匆匆使用了事,现在想想真的很惭愧!
以后引入 Gem
最起码要将文档通读一边,有能力更应该通读源码!
谨记!
修改文件/etc/ssh/ssh_config
, 添加如下代码:
1
|
|