• 另外,你可以使用 navbar-static-top:

    1. 优势:不用写额外的 padding-top
    2. 局限:不会固定在顶部
  • 验证 github 的 SSH 问题 at 2013年03月10日

    #14 楼 @zjnxzy 这是一个可能的原因,但我不觉得是你这里的问题,因为你的公钥已经被服务器接受了的。请看这几行代码,大致意思如下:

    • 可以继续进行的验证:publickey
    • 下一个验证方法:publickey
    • 尝试私钥:identity(这里很奇怪,我不知道 identity 是什么,应该不存在这个私钥文件)
    • 提供公钥:id_rsa(这里是正常的)
    • 服务器接受了 ssh-rsa 公钥
    • 输入 passphrase
    • 尝试私钥:dsa(???为什么 rsa 没通过,又该去尝试 dsa 了?发生了什么事儿?)
    • 没有其他的方法可以尝试了
    • 授权拒绝(publickey)

    OK,那么如果你认为是你复制的公钥格式有问题,这很好解决,重新复制一遍即可。记住,公钥的内容就是长长的一行,没有其他的,你把两头的空格与空行都删除就行了。

  • 验证 github 的 SSH 问题 at 2013年03月10日

    #12 楼 @zjnxzy

    我上传了我这边的结果,和你的最后几行做个对比,奇怪的是为什么在你那里寻找私钥的时候,SSH 尝试了 identityid_dsa,但就是没有尝试 id_rsa

    我看出了问题发生的地方,但是我不明白为什么会这样,我也不清楚 .ssh/identity是怎么回事,我只能帮你指出奇怪的地方在哪里,需要你自己进行进一步的排查。

    目前我能想到的一个方法是使用 dsa 生成你的密钥试试,像这样:

    $ ssh-keygen -t dsa -C "[email protected]"
    

    我猜想,既然在你哪里 SSH 会去尝试 dsa 私钥,那么也许这样可以成功?

  • 验证 github 的 SSH 问题 at 2013年03月10日

    #10 楼 @zjnxzy 请输入以下命令并反馈结果

    $ ssh -v [email protected]

  • 验证 github 的 SSH 问题 at 2013年03月10日

    #5 楼 @zjnxzy 这里看没什么问题

  • 验证 github 的 SSH 问题 at 2013年03月10日

    #4 楼 @zjnxzy 好吧,要不然你这样,重来一次,这次生成的时候不要输入 passphrase 试试能否成功;若是成功就证明问题出在 passphrase 身上,那时候你再添加 passphrase 即可:

    # 修改 passphrase 的命令
    $ ssh-keygen -f KEY_NAME -p PASSPHRASE
    # or
    $ ssh-keygen -f KEY_NAME -p -P OLD_PASS -N NEW_PASS
    
  • Teahour.fm 英文内容 at 2013年03月10日

    非常期待!一定要教嘉宾讲几句中文啊~

  • 验证 github 的 SSH 问题 at 2013年03月10日

    #2 楼 @zjnxzy 如果你确认你的 passphrase 是对的,为何像上图那样要你输三次呢?

    另外,最后一行显示授权被拒的原因是 public key,你确认一下在你的 ~/.ssh 下究竟有几对 keys?你确定你在 Github 上使用的是 id_rsa.pub 这个公钥吗?

    如果不是(这就解释得通了),那么请试一下 $ ssh-add KEY_NAME 把另外的 key 也加进来,然后再去尝试连接。

  • 验证 github 的 SSH 问题 at 2013年03月10日

    图片?

  • #17 楼 @keating 是 Array 的实例,等同于 Arrar.new 一个。看了你的疑问我忽然反应过来,我在第一篇回答里解释默认 subject 时为什么让人看不懂了,因为我漏了 .new ……

  • #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

    使用 should 写测试,理解测试的时候往往得把自己代入到语境之中,把“我”想象成“subject”。因为它读起来就好像:

    “我(subject) 应当(should) 怎样怎样(to do/be something)”。

    如果语境没有变化或者变化很小,那么读起来还不算太难懂,但如果在一个测试中有多个对象交互存在的话,要去分析和理解 subject 就会给阅读者带来很大的负担。

    expect

    使用 expect 语法,要求显式的指明 subject 是谁,而“我”则不变——始终是开发者自己,这样一来就会从主观陈述句式变成客观祈使句式,读起来就好像:

    “我(读代码的人)期望(expect)你(subject)怎样怎样(to do/be something)”。

    由于“我”是恒定不变的,且 subject 必须显式指明,所以即使语境的变化非常频繁,也不会给阅读者带来额外的负担。

    RSpec 正在进行时

    特别是在 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 是很灵活的,可以说没有它做不到,只有你想不到,初学者不妨多用用,细细体会如何写出简明却极富表达力的测试用例吧。

  • letsubject 很像,同出一源,都是通过委托来定义一个消息的接收方,这句话的意思可以理解成:为某个方法调用(此调用的结果是一个对象,这一点毋庸置疑,因为一切都是对象)绑定一个“名字”(一般用 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&blocklet,于是返回给我们一个 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 有更全面和深入的了解。

  • #2 楼 @zlx_star 嗯,忘了加 http 😄

  • feature specs 不是单纯的 integration test,不建议这么改;RSpec 和 Capybara 联手引入 feature specs 不是为了取代 request specs,别误会了这一点,建议读一下 RSpec Rails Documentation

  • #5 楼 @naitnix 有一点很奇怪,既然你这么喜欢用 less,为何要用 sass 化的 bootstrap gem?既然用了 sass 语法的 bootstrap,为啥不用 sass 写?何必给自己找麻烦呢?

  • 合买 CleanMyMac (已经凑满) at 2013年03月06日

    这软件真的……没啥用处

  • 求推荐机械键盘 at 2013年03月06日

    DasKeyboard Professional for Mac

    其实我对机械键盘不是很爽,但这款我用着(配合 Mac)非常爽,不能完全满足楼主的要求,仅供参考

  • 求职一份 at 2013年03月06日

    真的是哥们儿?见 id

  • #7 楼 @Teddy 谁说没有 badge 啊,我怀疑你没有仔细看官方文档吧?

  • #6 楼 @Sunnyroger 是,我是即兴写的,就好像平时和人聊天那样,还真的没存档。。。相类似的坑我在知乎也挖过几个,实在是没精力去填啊……

  • 初用 MacBook at 2013年03月03日

    @yeetim 用 Ubuntu 的时候,是不是装什么软件包都要用 apt-get?同样的道理,Mac 下也有这样的包管理器。如果你懒得自己编译的话,就去安装 Homebrew 或者 MacPort。

    凡事应该学会举一反三嘛……

  • 其实这也不算 bug 吧,教材毕竟都有时效性,可是 gems 却在不停更新换代。建议不要一切以教材为准,出现问题的话直接去看看相关的 changelogs 和 issues

  • 发几个字体效果 at 2013年03月02日

    Monaco +1

  • #19 楼 @leopku 咋?你也崩溃?时间久了我实在是想不起来,而且我后来又根据需要去掉了好些个默认的插件,这会儿我也无法确定去掉的这些哪两个是当时造成崩溃的罪魁祸首。

    我建议你按照自己的需求调整下插件吧,只保留不能不要的,其他的一概都去掉,直到用到再说。这样看看稳定性如何。

  • 不要偷懒解决吧,否则用 4 的意义何在?看看文档就知道了,这是一个明显的变化。

  • @lgn21st 我用 brew 安装的 vim 也出现过你说的崩溃等问题,而且我更新比较勤快,只要 brew 有新版本就会立刻更新,慢慢地我发现有的版本会崩溃,有的就不会。于是我把所有的插件都关了,使用默认配置然后换了好几个版本不停地试,都不崩溃了。后来我就采用排查法,一个一个插件的式,最终去掉了两个默认(spf-13)的插件,从此以后就再也不崩溃了……这事儿足足折腾了一个月有余啊

  • 如何来学习一个知识点 at 2013年02月26日

    #2 楼 @TsingHan 哦,这里有一点值得说明。我做例子的时候是不看原文的,我比较倾向于先读懂(工作方法,原理等),然后自己去写,并且做一个和原文不一样的例子。遇到的问题还是不看原文,而是利用搜索引擎或是 API 文档去解决。实在是卡住动不了了(这种情况很少)之后,才回去看原文,也不是只看一部分,而是重头再精读一遍,继续理解其工作方法和原理,然后确保脑中有了完整的 solution 之后再回去完成、