另外,你可以使用 navbar-static-top:
#14 楼 @zjnxzy 这是一个可能的原因,但我不觉得是你这里的问题,因为你的公钥已经被服务器接受了的。请看这几行代码,大致意思如下:
OK,那么如果你认为是你复制的公钥格式有问题,这很好解决,重新复制一遍即可。记住,公钥的内容就是长长的一行,没有其他的,你把两头的空格与空行都删除就行了。
我上传了我这边的结果,和你的最后几行做个对比,奇怪的是为什么在你那里寻找私钥的时候,SSH 尝试了 identity
和 id_dsa
,但就是没有尝试 id_rsa
。
我看出了问题发生的地方,但是我不明白为什么会这样,我也不清楚 .ssh/identity
是怎么回事,我只能帮你指出奇怪的地方在哪里,需要你自己进行进一步的排查。
目前我能想到的一个方法是使用 dsa 生成你的密钥试试,像这样:
$ ssh-keygen -t dsa -C "[email protected]"
我猜想,既然在你哪里 SSH 会去尝试 dsa 私钥,那么也许这样可以成功?
$ ssh -v [email protected]
非常期待!一定要教嘉宾讲几句中文啊~
图片?
#16 楼 @zw963 不是你的想法和我的不太一样,而是这两个框架不一样,你可能只熟悉 MiniTest,我则因为两者都用,所以对语义在 RSpec 中的作用更熟悉些。比较一下二者,最贴切的一句话就是:RSpec 是 DSL,MiniTest 是 Ruby;对于 Ruby 开发者来说,MiniTest 足够了,但是 RSpec 可以写出即使不懂 Ruby 语言也能看得懂的 specs(the new feature specs for example, which is my favorite improvement)。
这其实跟什么时代没关系,如果我在写一个小型项目,参与者都是 Ruby 程序员的时候,我们就用 MiniTest,因为在此种情形下,subject 和 let 有没有区别不重要,大家心里都清楚。而在这个帖子里,我写得复杂不是为了给某个人看的,而是给所有初学者解释 RSpec 使用 DSL 对我们意味着什么,能让我们获得什么。
多插几句,关于 should 和 expect 的区别。很多开发者不喜欢 expect,因为他们用 should 可以写出更简短的测试用例,那么为什么 RSpec 要选择 expect 呢?
问题就出在 should 虽然可以让测试用例很短,但是它隐藏了太多的语境信息,对于撰写测试的人来说还算 OK,但是让读测试的人很难跟得上语境的变化。
使用 should 写测试,理解测试的时候往往得把自己代入到语境之中,把“我”想象成“subject”。因为它读起来就好像:
“我(subject) 应当(should) 怎样怎样(to do/be something)”。
如果语境没有变化或者变化很小,那么读起来还不算太难懂,但如果在一个测试中有多个对象交互存在的话,要去分析和理解 subject 就会给阅读者带来很大的负担。
使用 expect 语法,要求显式的指明 subject 是谁,而“我”则不变——始终是开发者自己,这样一来就会从主观陈述句式变成客观祈使句式,读起来就好像:
“我(读代码的人)期望(expect)你(subject)怎样怎样(to do/be something)”。
由于“我”是恒定不变的,且 subject 必须显式指明,所以即使语境的变化非常频繁,也不会给阅读者带来额外的负担。
特别是在 RSpec 2.12.0 之后,由于和 Capybara 开发小组联手增加了 feature specs,expect 语法就越发重要了。我们知道,feature specs 相当于验收测试(acceptance test),它有一个很重要的功能就是能生成给不会写代码的人也能看懂的说明文档,should 虽然能减少一点代码量,但是在描述变化万千的用户界面交互时,一堆的 it should ... 只会让旁观者莫名其妙,因为他们不了解应用程序的内部构成,很难迅速分辨出 it 究竟是哪个 subject。换成 expect 之后,就变成了 expect(subject).to ... ,这样就很清楚了。
另外,should 的隐式调用能让开发者省去写 description 的工作,但是输出的文档却还是存在上述问题;expect 无法隐式调用 subject 了,于是 it 之后一定要写 description。初学者应当注意:既然你选择了使用 expect,那就不要再写 it 'should do something' ...
这样的 description 了,简单一点直接写 it 'do something' ...
即可。
一个良好的结构能够让任何人(包括不懂代码的人)都看得懂你要做什么:
feature "XX功能" do
background { condition } # 前置条件... 等同于 before
context "在某种情况下..." do
given(:object) { Object.new } # 给定某个参与者... 等同于 let
scenario "做某件事情..." do # 等同于 it,意思是“场景”
...
expect(subject).to do/be something # subject 是谁,你说了算
end
scenario "做另一件事情..." do
...
expect(subject).to do/be something
end
end
context "在另一种情况下..." do
given(:object) { Object.new }
scenario "做某件事情..." do
...
expect(subject).to do/be something
end
end
end
#12 楼 @zw963 没错,我的解释其实就是把简单的问题复杂化,因为我的目的不是为了简单地回答“let 和 subject 不一样”。
在一开始我就说了,let 和 subject 系出同源,从语言的角度来看它们都是 delegation,还真没什么不一样的。但是从语法,或者说从语义的角度上来讲就有区别了,这一点正如 @lgn21st 所补充的那样。
要理解这一点,我们可以问自己一个问题,为什么 RSpec 会受欢迎?众多原因当中,DSL 是一个非常重要的因素。DSL 本身只是对既有代码的一层封装,目的(之一)就是为了增加“可读性”。当我们写一段测试的时候,我们头脑里应该很清楚的知道谁是“主角”,谁是“配角”,尽管两者都是“角色”(所以你把 let 和 subject 再抽象一层就都是 delegator),但是分出主次就会让你的测试用例变得非常清楚,非常明确。
很多开发者(特别是英文不好,或是对语义无所谓的开发者)写测试仅仅是为了给自己加一层保护伞,只要目的达到了,写成什么样都无所谓;殊不知,测试本身也是对应用程序的一种注解,撰写良好的测试代码可以让其他人仅看测试就可以复刻你的 implementation,要达到这个层级,那就要求你对 DSL 有良好的理解和使用习惯。
对于最后一个例子让你产生的疑惑,我表示歉意,最后一个例子的确没能深刻表现出 let 和 subject 的一个微妙的区别,我再换用一个例子加以说明:
describe Array do
context 'when first created' do # 使用 context 的好处:语法上清除作用域;语义上体现语境变化
# 主角刚诞生,两手空空...
it { should have(0).items } # => pass
its(:size) { should == 0 } # => pass
end
context 'update subject' do
subject { Array.new(5) } # => 主角升级了,HP +5... >_<
it { should have(5).items } # => pass
its(:size) { should == 5 } # => pass
end
context 'wrong subject' do
subject { String.new('5') } # => 另外一部戏的主角走错了戏场... -_-!
it { should have(5).items } # => fail... (导演)“你哪儿来的?”
end
context 'let will not work' do
let(:new_array) { Array.new(5) } # => 配角登场,妄图抢夺主角的地位
it { should have(5).items } # => 结果... 悲催了 T_T
its(:size) { should == 5 } # => fail
# 因为切换了语境,subject 又变成了初始值
it { should have(0).items } # => 主角的地位还是很稳固的 ^_^
its(:size) { should == 0 } # => pass
end
context 'let becomes subject' do
let(:new_array) { Array.new(5) } # => 配角再度登场,并且贿赂了一下导演(我...)
subject { new_array } # => 于是导演让配角试一下主角的戏份
it { should have(5).items } # => 嗯... 表现不错!
its(:size) { should == 5 } # => pass
end
end
之所以前文的例子没能这样清晰的表现,是因为我使用了 expect 语法(主动式的预期);新版本的 RSpec 推荐开发者使用 expect 替代过去的 should,这种语法貌似更受欢迎(驱使开发者用祈使句来撰写测试说明),唯一的缺憾就是丧失了上例中使用 should 带来的简洁性。因为 expect 语法要求显式指明 subject,所以隐式调用的特点就没能在前文的例子中表现的很透彻。
此外,例子中也体现了 context 和 its 的用法,RSpec 是很灵活的,可以说没有它做不到,只有你想不到,初学者不妨多用用,细细体会如何写出简明却极富表达力的测试用例吧。
let
和 subject
很像,同出一源,都是通过委托来定义一个消息的接收方,这句话的意思可以理解成:为某个方法调用(此调用的结果是一个对象,这一点毋庸置疑,因为一切都是对象)绑定一个“名字”(一般用 symbol
),于是在后面的测试样例中,我们可以用这个名字来指代它。最直接的好处就是可以让代码更精炼,提高可读性,减少重复。
说它们同出一源,可以通过源码获知:
def let(name, &block)
::RSpec::Core::MemoizedHelpers.module_for(self).define_method(name, &block)
define_method(name) do
__memoized.fetch(name) { |k| __memoized[k] = super() }
end
end
简单地说,我们传递了 name
和 &block
给 let
,于是返回给我们一个 defined method;再看 subject
:
def subject(name=nil, &block)
let(:subject, &block)
alias_method name, :subject if name
end
see? subject
本身就是 let
,只不过如果我们给了 name
的话,最后会把这个 name
作为最终结果的 alias_method
为什么要这么做呢?再来看一处源码便知:
def should(matcher=nil, message=nil)
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
end
原来,subject
是用来配合 should
进行隐式调用的,在这里何为隐式调用?举个例子:
# 不用 subject
describe "Checking Account initialization" do
it "should have balance with $50" do
account = CheckingAccount.new(Money.new(50, :USD))
account.should have_a_balance_of(Money.new(50, :USD)) # should_have_a_balance 是自定义 matcher
end
end
# 使用 subject
describe CheckingAccount, "with $50" do
# 直接用的 Class Name,若此时没有显式定义 subject,那么默认的 subject 就是 CheckingAccount.new,可通过在代码中输出 subject 获知
subject { CheckingAccount.new(Money.new(50, :USD)) }
it { should have_a_balance_of(Money.new(50, :USD)) }
end
如果你要使用主动式的 expectation,那么可以给 subject
起名字(非隐式调用):
describe "Checking Account initialization" do
subject (:account) { CheckingAccount.new(Money.new(50, :USD)) }
it "has $50 balance" do
expect(account).to have_a_balance_of(Money.new(50, :USD))
end
it "has a balance attribute which equals the starting balance" do
expect(account.balance).to eq(Money.new(50, :USD))
end
end
上面的例子里,Money.new(50, :USD)
明显重复了很多次,但它又不是我们要测试的主题(subject 就是主题的意思),此时就是应该使用 let
的时候了:
# 重构上面的例子
describe "Checking Account initialization" do
let(:starting_balance) { Money.new(50, :USD) }
subject(:account) { CheckingAccount.new(starting_balance) }
it "has $50 balance" do
expect(account).to have_a_balance_of(starting_balance)
end
it "has a balance attribute which equals the starting balance" do
expect(account.balance).to eq(starting_balance)
end
end
怎么样?感觉清晰多了吧?
#3 楼 @golden05 我不知道,应该是没有,要想有也不是不可以,不过要去修改 rails 的 generator 吧?
我觉得没必要有一个专门的命令,因为 feature specs 经常会跨越多个 view 或 model 所 handle 的 范围,它不是框架的一个部分,而是对整个应用的某个“方面”的测试。
request specs 虽然也类似,但它毕竟是 rails 内置的 integration test 的 wrapper,rails 本身就有 integration test 的机制,所以 generator 提供了创建目录和文件的功能也实属正常。
feature specs 针对的是应用外在的表现、行为以及结果,它相当于 user test 或者 acceptance test。以前这个层面的测试通常都会用 Cucumber 或类似的工具来完成,capybara 原本是 cucumber 的绝配(用来模拟用户在 UI 上的交互操作,并帮助返回期望的测试结果),可是 rails 的开发者们更偏爱灵活强大的 RSpec,于是逐渐演变成了使用 request specs 来代替验收测试的部分功能。
这样做其实是不妥的,request specs 应当正对某种应用场景的内部处理过程进行测试,在其内部测试用例调用的都应该是 rails 的内置方法或专门为 integration test 提供的 helpers,capybara 的频繁介入是因为开发者想要通过操作或监视外部(UI)的表现来验证内部的处理机制。久而久之,request specs 已经远远偏离了它存在的初始意义,而相应的 integration test 变得越来越不纯粹。
正是由于这个原因,当 capybara 进行到 2.0 的时候,和 RSpec 的开发团队做出了共同的决定,为 RSpec 增加新的 feature level specs,并且把 capybara 的使用限定在 feature specs 里,还特别为 feature specs 创建了很多 DSL alias,比如 background => before, given => let, scenario => it, feature => describe 等,这么做的目的就是为了给开发者提供一个更加纯粹的验收测试语境,从而让偏爱 RSpec 的开发者有足够好的工具来取代 Cucumber。
我正在录制一期关于 RSpec 的教学视频,针对初学者的,一开始就特别讲述了上述的一些概念,等我制作好了会分享给大家,希望大家对于 RSpec Capybara 有更全面和深入的了解。
feature specs 不是单纯的 integration test,不建议这么改;RSpec 和 Capybara 联手引入 feature specs 不是为了取代 request specs,别误会了这一点,建议读一下 RSpec Rails Documentation
这软件真的……没啥用处
DasKeyboard Professional for Mac
其实我对机械键盘不是很爽,但这款我用着(配合 Mac)非常爽,不能完全满足楼主的要求,仅供参考
真的是哥们儿?见 id
#6 楼 @Sunnyroger 是,我是即兴写的,就好像平时和人聊天那样,还真的没存档。。。相类似的坑我在知乎也挖过几个,实在是没精力去填啊……
@yeetim 用 Ubuntu 的时候,是不是装什么软件包都要用 apt-get?同样的道理,Mac 下也有这样的包管理器。如果你懒得自己编译的话,就去安装 Homebrew 或者 MacPort。
凡事应该学会举一反三嘛……
其实这也不算 bug 吧,教材毕竟都有时效性,可是 gems 却在不停更新换代。建议不要一切以教材为准,出现问题的话直接去看看相关的 changelogs 和 issues
Monaco +1
不要偷懒解决吧,否则用 4 的意义何在?看看文档就知道了,这是一个明显的变化。
@lgn21st 我用 brew 安装的 vim 也出现过你说的崩溃等问题,而且我更新比较勤快,只要 brew 有新版本就会立刻更新,慢慢地我发现有的版本会崩溃,有的就不会。于是我把所有的插件都关了,使用默认配置然后换了好几个版本不停地试,都不崩溃了。后来我就采用排查法,一个一个插件的式,最终去掉了两个默认(spf-13)的插件,从此以后就再也不崩溃了……这事儿足足折腾了一个月有余啊