Not sure if this falls into the useful or into the weird category, but I’m beginning to do code generation in my behavior specifications…

Take a look at the behavior specification:

text/unit/transaction_state_test.rb

1 require File.dirname(FILE) + /../test_helper
2
3 class TransactionStateOrderingTest < Test::Unit::TestCase
4 TransactionState::StatusNames.each do |status|
5 define_method("test_#{status}query_method") do
6 assert TransactionState.send(status).send("#{status}?"), status
7 (TransactionState::StatusNames – [status]).each do |inner_status|
8 assert !TransactionState.send(status).send("#{inner_status}?"), inner
status
9 end
10 end
11 end
12 end

And the corresponding implementation:

app/models/transaction_state.rb

1 class TransactionState
2 StatusNames = %w(started approved processed completed cancelled).freeze
3
4 attr_reader :name, :index
5 alias_method :to_s, :name
6
7 def initialize(name)
8 name</span>, <span class="iv">index = name.to_s, StatusNames.index(name.to_s)
9 raise "Unknown status name: #{name.inspect}" unless index</span> <span class="no"><strong>10</strong></span> <span class="r">end</span> <span class="no">11</span> <span class="no">12</span> <span class="c"># Generate instance methods to query the state of this instance.</span> <span class="no">13</span> <span class="c"># Generates #started? and #completed?, among others.</span> <span class="no">14</span> <span class="co">StatusNames</span>.each <span class="r">do</span> |status| <span class="no"><strong>15</strong></span> define_method(<span class="s"><span class="dl">&quot;</span><span class="il"><span class="idl">#{</span>status<span class="idl">}</span></span><span class="k">?</span><span class="dl">&quot;</span></span>) <span class="r">do</span> <span class="no">16</span> <span class="pc">self</span>.name == <span class="co">TransactionState</span>.send(status).name <span class="no">17</span> <span class="r">end</span> <span class="no">18</span> <span class="r">end</span> <span class="no">19</span> <span class="no"><strong>20</strong></span> <span class="c"># Generate class methods to return pre-instantiated</span> <span class="no">21</span> <span class="c"># TransactionState objects, one per status name.</span> <span class="no">22</span> <span class="r">class</span> &lt;&lt; <span class="cl">self</span> <span class="no">23</span> <span class="cv">states_cache</span> = <span class="co">Hash</span>.new <span class="no">24</span> <span class="co">StatusNames</span>.each <span class="r">do</span> |status| <span class="no"><strong>25</strong></span> <span class="cv">states_cache</span>[status.to_sym] = <span class="co">TransactionState</span>.new(status) <span class="no">26</span> define_method(status) <span class="r">do</span> <span class="no">27</span> <span class="cv">@states_cache[status.to_sym]
28 end
29 end
30 end
31
32 Statuses = StatusNames.map {|status| TransactionState.send(status)}
33 end

Nothing too earth shattering, except for the specs. How do I ensure the specs were properly generated ? I didn’t, except for confirming that the number of tests and assertions went up as expected.

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on