<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>treehacker (Zhuang Zhang)</title>
    <link>https://ruby-china.org/treehacker</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>如何有效的在 RSpec 中 mock 由 SQL View 生成的 ActiveRecord</title>
      <description>&lt;p&gt;我们有很多的 report 是由 SQL 直接生成的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SaleReport&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="c1"&gt;#...&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sale_report"&lt;/span&gt;
  &lt;span class="c1"&gt;#..。&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 sale_report 是一个 postgresql view，由 10 个以上 table join 生成的，大概这个样子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT 
       table_a.id,
       #省略若干

FROM abstract_table_a table_a
       LEFT JOIN table_u table_u ON table_u.id = table_a.payer_id
  AND table_a.payer_type = 'Farmer'
       LEFT JOIN table_b table_b ON table_b.id = table_a.order_item_id
       #省略若干LEFT JOIN
  AND table_c.archived_at IS NULL
  AND table_c.staff_id IS NOT NULL
       LEFT JOIN users table_d ON table_d.p_id = orders.p_id
  AND table_d.archived_at IS NULL
    LEFT JOIN table_f table_f ON table_b.p_id = table_f.id
  AND table_b.p_type = 'SUV'
    LEFT JOIN table_l table_f_category ON table_f_category.id = table_f.table_l_id
    LEFT JOIN table_g table_g ON table_b.p_id = table_g.id
  AND table_b.p_type = 'CAR'
       LEFT JOIN table_l table_g_category ON table_g_category.id = table_g.table_l_id
       LEFT JOIN table_h ON table_b.p_id = table_h.id
  AND table_b.p_type = 'TRUCK'
       LEFT JOIN table_r ON table_h.treatment_id = table_r.id
       LEFT JOIN table_l table_h_category
                 ON table_h_category.id = table_r.table_l_id
       LEFT JOIN (
                   #省略若干行
                 ) AS table_x ON table_a.id = table_x.inv_id

       LEFT JOIN (
                   SELECT #省略若干行
                   FROM #省略若干行
                          INNER JOIN #省略若干行
                   WHERE#省略若干行
                 ) table_Z ON table_a.id = table_Z.inv_id
WHERE #省略若干行
ORDER BY #省略若干行;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望的是能在 rspec 中，就像用 facotrybot 一样能够&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;let!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sale_report&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我尝试过创建&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="ss"&gt;:sale_report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="no"&gt;SaleReport&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但得到 error&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ActiveRecord::StatementInvalid: PG::ObjectNotInPrerequisiteState: ERROR:  cannot insert into view "sale_report"
DETAIL:  Views that do not select from a single table or view are not automatically updatable.
HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.
: INSERT INTO "sale_report" DEFAULT VALUES RETURNING "id"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在如果想要写这个 report 的单元测试需要一个个的 creat join table 的 instance，非常的耗时，而且需要先理解 tables 之间的关系。&lt;/p&gt;

&lt;p&gt;请问有什么思路吗？有什么 gem 可以帮助实现吗&lt;/p&gt;</description>
      <author>treehacker</author>
      <pubDate>Mon, 26 Oct 2020 01:31:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/40510</link>
      <guid>https://ruby-china.org/topics/40510</guid>
    </item>
    <item>
      <title>有没有好的 report generate 解决方案</title>
      <description>&lt;p&gt;【也许我问了一个无解的问题】
我们是一个 SaaS 系统，现在提供一些基本的 filter 让用户生成报告，但近期发现用户的生成报告的需求越来越个性化，所以在思考有没有什么第三方软件可以让用户生成报告。
比如 Tableau，或者 aws 有没有此类服务？
或者有没有什么其他的方案&lt;/p&gt;</description>
      <author>treehacker</author>
      <pubDate>Thu, 23 Apr 2020 02:53:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/39776</link>
      <guid>https://ruby-china.org/topics/39776</guid>
    </item>
    <item>
      <title>RSpec 如何实现动态定义 be_matcher</title>
      <description>&lt;p&gt;第一次发文 不足望指正&lt;img title=":smiley:" alt="😃" src="https://twemoji.ruby-china.com/2/svg/1f603.svg" class="twemoji"&gt; &lt;/p&gt;

&lt;p&gt;在官方文档中&lt;a href="https://www.rubydoc.info/gems/rspec-rails/1.3.4/Spec/Rails/Matchers" rel="nofollow" target="_blank"&gt;https://www.rubydoc.info/gems/rspec-rails/1.3.4/Spec/Rails/Matchers&lt;/a&gt;，提到了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be_success&lt;/span&gt; &lt;span class="c1"&gt;#passes if response.success?&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be_redirect&lt;/span&gt; &lt;span class="c1"&gt;#passes if response.redirect?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但并没有提着个方法是从哪里来的，
接下来就详细讲解下
假设单元测试中一个断言为&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_api_get_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_success&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先会动态定义 be_success 方法，这个是通过/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/matchers.rb:953 的 BE_PREDICATE_REGEX 来定义的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;BE_PREDICATE_REGEX&lt;/span&gt;
        &lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BePredicate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;HAS_REGEX&lt;/span&gt;
      &lt;span class="c1"&gt;#....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后紧接着就会运行这个刚刚动态定义完的 be_success
紧接着就触发下面的追溯链 (TraceBack stack): &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;matches?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BePredicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;handle_matcher&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Expectations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PositiveExpectationHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;with_matcher&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Expectations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ExpectationHelper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;handle_matcher&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Expectations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PositiveExpectationHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;expectation_target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Expectations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ExpectationTarget&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstanceMethods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;my_api_get_user_spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最顶端的 RSpec::Matchers::BuiltIn::BePredicate#matches？重写了 matches？方法，重写 matches？方法是自定义 Matcher 的手段：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#actual在这里就是{Symbol}success?&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;matches?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@actual&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="c1"&gt;#actual is the instnce of my_api_get_user&lt;/span&gt;
  &lt;span class="vi"&gt;@block&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="c1"&gt;#block can be nil&lt;/span&gt;
  &lt;span class="n"&gt;predicate_accessible?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;predicate_matches?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;predicate_accessible？用于确认 method sucess 是否真的存在于 my_api_get_user
predicate_matches？的实现方式&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/be.rb:190&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predicate_matches?&lt;/span&gt;
    &lt;span class="n"&gt;method_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;present_tense_predicate&lt;/span&gt;
    &lt;span class="vi"&gt;@predicate_matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__send__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="vi"&gt;@args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="vi"&gt;@block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 actual.&lt;strong&gt;send&lt;/strong&gt;(method_name, *@args, &amp;amp;@block)
就是相当于，&lt;u&gt;send&lt;/u&gt;是 ruby 中元编程动态定义方法的核心&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;my_api_get_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="vi"&gt;@args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="vi"&gt;@block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果返回的是 true 的话，最终会在这里做判断 (可以在之前的回溯链中找到 handle_matcher [handler.rb:48])：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#/.rvm/gems/ruby-2.3.1/gems/rspec-expectations-3.7.0/lib/rspec/expectations/handler.rb:47&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle_matcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;ExpectationHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_matcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BuiltIn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PositiveOperatorMatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;initial_matcher&lt;/span&gt;
    &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;ExpectationHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:failure_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;ExpectationHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:failure_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 matcher.matches?(actual, &amp;amp;block) 返回为 true，则断言为 true，否则就调用 ExpectationHelper.handle_failure 来出来错误断言&lt;/p&gt;</description>
      <author>treehacker</author>
      <pubDate>Thu, 04 Apr 2019 02:46:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/38342</link>
      <guid>https://ruby-china.org/topics/38342</guid>
    </item>
    <item>
      <title>请问有没有 job board open source project</title>
      <description>&lt;p&gt;就是能让求职者和招聘者发布信息的网站。
知道有一个，但太过于简单：&lt;a href="https://github.com/blairanderson/open-source-job-board" rel="nofollow" target="_blank"&gt;https://github.com/blairanderson/open-source-job-board&lt;/a&gt;
有没成熟一点的，感谢&lt;/p&gt;</description>
      <author>treehacker</author>
      <pubDate>Tue, 15 Aug 2017 12:47:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/33834</link>
      <guid>https://ruby-china.org/topics/33834</guid>
    </item>
  </channel>
</rss>
