anaphoric_case 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -0
- data/Gemfile +13 -0
- data/README.md +56 -0
- data/Rakefile +53 -0
- data/anaphoric_case.gemspec +27 -0
- data/lib/anaphoric_case/version.rb +3 -0
- data/lib/anaphoric_case.rb +137 -0
- data/spec/anaphoric_case_spec.rb +454 -0
- data/spec/spec_helper.rb +85 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#Anaphoric Case
|
2
|
+
|
3
|
+
Provides an anaphoric if, and anaphoric case-like construct as kernel methods.
|
4
|
+
|
5
|
+
###Examples:
|
6
|
+
|
7
|
+
English: If the dog is on fire, put it out!
|
8
|
+
|
9
|
+
Ruby: If the dog is on fire, put the dog out!
|
10
|
+
|
11
|
+
This is all well and good provided dog is a simple reference, but if the dog
|
12
|
+
is something more complicated, you wind up either having to do an assign
|
13
|
+
in the if condition, or assign to a temporary variable before you extinguish
|
14
|
+
Fido.
|
15
|
+
|
16
|
+
This is similar to the use case for the andand gem. It's especially handy
|
17
|
+
when the dog being on fire is not a simple binary condition. If Rin-tin-tin
|
18
|
+
can summon help depending upon his dire situation, like if dog.fire also says
|
19
|
+
which *part* of the dog is on fire, or where he is drowning at.
|
20
|
+
|
21
|
+
# Listen for sounds of distress
|
22
|
+
if dog.fire
|
23
|
+
owner.tell(dog.fire)
|
24
|
+
elsif dog.drowning
|
25
|
+
owner.tell(dog.drowning)
|
26
|
+
elsif dog.hungry
|
27
|
+
owner.tell(dog.hungry)
|
28
|
+
end
|
29
|
+
|
30
|
+
An anaphoric if doesn't need to call `dog.fire` again in the executed block.
|
31
|
+
|
32
|
+
aif(dog.fire) { |it| owner.tell(it) }
|
33
|
+
|
34
|
+
If you have multiple conditions, you probably want to use the switch/on construct
|
35
|
+
|
36
|
+
owner.tell(switch do
|
37
|
+
on dog.fire
|
38
|
+
on dog.drowing
|
39
|
+
on dog.hungry
|
40
|
+
end)
|
41
|
+
|
42
|
+
Here, the dog will tell his owner the first condition he encounters. `switch` can
|
43
|
+
also behave like a regular case statement (albiet with fallthrough) if you like.
|
44
|
+
|
45
|
+
switch dog.name do
|
46
|
+
on /Rover/ { |it| "Come on over #{it}"}
|
47
|
+
on /Fido/ { |it| "Give #{it} a bone"}
|
48
|
+
on /Rin-Tin-Tin/ { |it| "#{it} is frequently mistaken for Lassie"}
|
49
|
+
end
|
50
|
+
|
51
|
+
If the switch parameter takes a block, it will be passed into the block as an optional
|
52
|
+
block parameter. In addition, the `on` method will yield the parameter object of the
|
53
|
+
`switch` method, rather than it's own parameter.
|
54
|
+
|
55
|
+
If `switch` is called with an explicit receiver, it acts somewhat like `tap` in that the
|
56
|
+
block is executed in the context of the receiver.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
9
|
+
spec.rspec_opts = "-d"
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new(:spec_with_report) do |spec|
|
13
|
+
spec.fail_on_error = false
|
14
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
15
|
+
spec.rspec_opts = "--format html --out report/tests/report.html"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run all specs, profile the code, and generate a coverage report"
|
19
|
+
task :report do
|
20
|
+
Dir.mkdir "report" unless File.exists? "report"
|
21
|
+
Dir.mkdir "report/profile" unless File.exists? "report/profile"
|
22
|
+
Dir.mkdir "report/tests" unless File.exists? "report/tests"
|
23
|
+
File.open "report/index.html","w" do |f|
|
24
|
+
f.write <<-HTML
|
25
|
+
<html>
|
26
|
+
<body>
|
27
|
+
<h1> Status Report </h1>
|
28
|
+
<p>
|
29
|
+
<a href="coverage/index.html"> Coverage </a>
|
30
|
+
</p>
|
31
|
+
<p>
|
32
|
+
<a href="profile/profile.graph.html"> Graph Speed Profile </a><br/>
|
33
|
+
<a href="profile/profile.stack.html"> Speed Profile </a>
|
34
|
+
</p>
|
35
|
+
<p>
|
36
|
+
<a href="test/report.html"> Test Report </a>
|
37
|
+
</p>
|
38
|
+
</body>
|
39
|
+
</html>
|
40
|
+
HTML
|
41
|
+
end
|
42
|
+
ENV["REPORT"] = "1"
|
43
|
+
Rake::Task[:spec_with_report].invoke
|
44
|
+
ENV["REPORT"] = ""
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Delete the report directory"
|
48
|
+
task :clear_report do
|
49
|
+
`rm -rf report`
|
50
|
+
end
|
51
|
+
|
52
|
+
task :default => :spec
|
53
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "anaphoric_case/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "anaphoric_case"
|
7
|
+
s.version = AnaphoricCase::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Stephen Prater"]
|
10
|
+
s.email = ["me@stephenprater.com"]
|
11
|
+
s.homepage = "http://github.com/stephenprater/anaphoric_case"
|
12
|
+
s.summary = %q{Provides a simple anaphoric case statement (called "switch/on")}
|
13
|
+
s.description = %q{You have twenty or so methods, and you want to call the first one that returns something other than nil,
|
14
|
+
If the dog is on fire, put it out.}
|
15
|
+
|
16
|
+
s.rubyforge_project = "anaphoric_case"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
|
23
|
+
s.add_development_dependency('rspec')
|
24
|
+
s.add_development_dependency('simplecov')
|
25
|
+
s.add_development_dependency('rspec-prof')
|
26
|
+
s.add_development_dependency('i18n')
|
27
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
module AnaphoricCase
|
4
|
+
@__nest = 0
|
5
|
+
class << self
|
6
|
+
# @private
|
7
|
+
attr_accessor :__nest
|
8
|
+
|
9
|
+
def switch_stack
|
10
|
+
Thread.current[:__switch_stack] ||= []
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Kernel
|
16
|
+
# aif - anaphoric if.
|
17
|
+
#
|
18
|
+
# English is nice, in that if you want to make a conditional, you can say
|
19
|
+
# "if the dog is on fire, then put it out!" to say the equivalent thing in Ruby
|
20
|
+
# you say "if the dog is on fire, then put the dog out." - which is fine, if something
|
21
|
+
# of a hassle sometimes. The "it" in the first example is an anaphor -
|
22
|
+
#
|
23
|
+
# @param [Object] result - any object, although it generally take the form of an expression.
|
24
|
+
# @return [Object, FalseClass] returns the results of the block if the paramater was
|
25
|
+
# truthy (ie, it wasn't `nil` or `false`) or FalseClass if it wasn't
|
26
|
+
# @yield [Object] yields the parameter into a block if it's truthy, the block can optionally return
|
27
|
+
# a different value, which will become the return value of #aif
|
28
|
+
def aif result = true, &block
|
29
|
+
if result
|
30
|
+
unless block.nil? then result = block.call(*[result].slice(0,block.arity)) end
|
31
|
+
return result
|
32
|
+
end
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
# switch - anaphoric case construct
|
37
|
+
#
|
38
|
+
# this is basically a form of sugar over a whole list of || operators or +or+ statements
|
39
|
+
# @example
|
40
|
+
# thing = switch do
|
41
|
+
# on object.quick_method_that_might_return_nil
|
42
|
+
# on object.medium_method_that_might_return_nil
|
43
|
+
# on object.slow_method_that_might_return_nil
|
44
|
+
# on { raise 'everything returned nil!' }
|
45
|
+
# end
|
46
|
+
# # thing will be equal to results of the first method that
|
47
|
+
# # didn't return nil
|
48
|
+
#
|
49
|
+
# This prevents you from either writing a difficult to read list of || operators, or
|
50
|
+
# having to construct lazily evaluated list of methods and parameters in order to do
|
51
|
+
# the cheapest thing.
|
52
|
+
# @param [Object] object -
|
53
|
+
# parameter of all +on+ calls will be compared to this object using +===+
|
54
|
+
# @yield A block during which the +on+ method will be available - the block is required.
|
55
|
+
# The block is always executed in the context of the receiver of the +switch+ method
|
56
|
+
# so most of the time this is +self+ but if you call +switch+ with an explicit receiver
|
57
|
+
# it acts somewhat like the {Object#tap} method
|
58
|
+
# @raise [ArgumentError] I tried to tell you the callable or block was required
|
59
|
+
# @return [Object, FalseClase] the results of the first call to +on+ that returned
|
60
|
+
# anything truthy, or of its block
|
61
|
+
def switch object = nil, &block
|
62
|
+
block = block.dup # enable cross thread sharing of blocks
|
63
|
+
|
64
|
+
AnaphoricCase.__nest += 1
|
65
|
+
|
66
|
+
raise ArgumentError, "switch requires a block" unless block_given?
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def on result = true, &block
|
70
|
+
# the current switch block in this thread
|
71
|
+
it = AnaphoricCase.switch_stack.last.instance_eval { @it }
|
72
|
+
|
73
|
+
begin
|
74
|
+
if it and (result === it or result == true)
|
75
|
+
result = Kernel.aif it, &block
|
76
|
+
throw :result, result if result
|
77
|
+
elsif result and not it
|
78
|
+
result = Kernel.aif result, &block
|
79
|
+
throw :result, result if result
|
80
|
+
end
|
81
|
+
rescue ArgumentError => e
|
82
|
+
if e.message =~ /throw :result/
|
83
|
+
raise NameError, "on without associated switch"
|
84
|
+
else
|
85
|
+
raise e
|
86
|
+
end
|
87
|
+
end
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
block.instance_eval do
|
93
|
+
@it = object
|
94
|
+
end
|
95
|
+
AnaphoricCase.switch_stack << block
|
96
|
+
|
97
|
+
res = catch :result do
|
98
|
+
if block.arity > 0
|
99
|
+
self.instance_exec(object, &block)
|
100
|
+
else
|
101
|
+
self.instance_eval(&block)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
res ? res : false
|
105
|
+
|
106
|
+
ensure
|
107
|
+
AnaphoricCase.__nest -= 1
|
108
|
+
Thread.current[:__switch_stack].pop if object
|
109
|
+
if AnaphoricCase.__nest == 0
|
110
|
+
class << self
|
111
|
+
#you can get here without the on method being defined,
|
112
|
+
#so rescue this silently if so.
|
113
|
+
remove_method :on rescue nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# on
|
119
|
+
#
|
120
|
+
# the +on+ method is available only in the {#switch} block - it basically
|
121
|
+
# functions exactly like the aif method.
|
122
|
+
#
|
123
|
+
# The first +on+ method whose argument is truthy will execute it's block and
|
124
|
+
# cause switch to return the value of the block.
|
125
|
+
# @see #aif
|
126
|
+
# @see #switch
|
127
|
+
# @param [Object] result - any object, normally an expression
|
128
|
+
# it defaults to true, so you can use an +on+ without an argument as a default clause
|
129
|
+
# @raise [NameError] if called from outside of a switch block or across
|
130
|
+
# the stack rewinding barrier
|
131
|
+
# @yield [Object] yields the parameter into the block, which can optionally
|
132
|
+
# return a different value. <br/> if the block of an +on+ returns +nil+ or +false+,
|
133
|
+
# evaluation will continue at the next call to +on+. If the block is ommitted
|
134
|
+
# the parameter passed to +on+ will be returned (by {#switch})
|
135
|
+
def on result=true; super; end
|
136
|
+
remove_method :on
|
137
|
+
end
|
@@ -0,0 +1,454 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'anaphoric_case'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
describe "it is an anaphoric case" do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
class Harness
|
10
|
+
def initialize
|
11
|
+
@array = [1,2,3,4,5]
|
12
|
+
@hash = { :bacon => "cheese", :pie => "sky", :snoopy => "pizza" }
|
13
|
+
@string = "That is quite a mustache you've got there, sheriff."
|
14
|
+
@snitch = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def count
|
18
|
+
@array
|
19
|
+
end
|
20
|
+
|
21
|
+
def flavors thing
|
22
|
+
@hash[thing]
|
23
|
+
end
|
24
|
+
|
25
|
+
def compliment
|
26
|
+
@string
|
27
|
+
end
|
28
|
+
|
29
|
+
def snitch
|
30
|
+
@snitch
|
31
|
+
end
|
32
|
+
|
33
|
+
def narc
|
34
|
+
@snitch += 1
|
35
|
+
if @string == "I committed regicide"
|
36
|
+
true
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def cover_evidence
|
43
|
+
@string = "I committed regicide"
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
before :each do
|
50
|
+
@test = Harness.new
|
51
|
+
end
|
52
|
+
|
53
|
+
profile :all do
|
54
|
+
|
55
|
+
it "provides a simple aif method" do
|
56
|
+
res = nil
|
57
|
+
aif(5 + 4) { |it| res = it }
|
58
|
+
res.should == 9
|
59
|
+
|
60
|
+
res = nil
|
61
|
+
res = aif(5 + 4) { 'okay!' }
|
62
|
+
res.should == 'okay!'
|
63
|
+
|
64
|
+
res = aif(5 < 4) { 'monkey' }
|
65
|
+
res.should == false
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should return the first object which is truthy" do
|
69
|
+
res = switch do
|
70
|
+
on 1
|
71
|
+
on false
|
72
|
+
on nil
|
73
|
+
end
|
74
|
+
res.should == 1
|
75
|
+
|
76
|
+
res = switch do
|
77
|
+
on false
|
78
|
+
on 2
|
79
|
+
on nil
|
80
|
+
end
|
81
|
+
|
82
|
+
res.should == 2
|
83
|
+
|
84
|
+
res = switch do
|
85
|
+
on false
|
86
|
+
on nil
|
87
|
+
on :bob
|
88
|
+
end
|
89
|
+
res.should == :bob
|
90
|
+
|
91
|
+
res = switch do
|
92
|
+
on false
|
93
|
+
on [1,2,3,4,5]
|
94
|
+
end
|
95
|
+
res.should == [1,2,3,4,5]
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
it "is lazy" do
|
100
|
+
res = switch do
|
101
|
+
on @test.narc
|
102
|
+
on @test.cover_evidence
|
103
|
+
on @test.narc
|
104
|
+
on @test.compliment
|
105
|
+
end
|
106
|
+
res.should == true
|
107
|
+
@test.snitch.should == 2
|
108
|
+
end
|
109
|
+
|
110
|
+
it "can execute anaphoric blocks using on" do
|
111
|
+
res = switch do
|
112
|
+
on(false) { |it| it.blah_blah } #this would raise an error if it was ever called
|
113
|
+
on(1) { |it| it + 1 }
|
114
|
+
end
|
115
|
+
res.should == 2
|
116
|
+
end
|
117
|
+
|
118
|
+
it "can have an else clause by way of on with no parameters" do
|
119
|
+
res = switch do
|
120
|
+
on false
|
121
|
+
on nil
|
122
|
+
on { 5 }
|
123
|
+
end
|
124
|
+
res.should == 5
|
125
|
+
|
126
|
+
res = switch do
|
127
|
+
on 1
|
128
|
+
on { 1 }
|
129
|
+
end
|
130
|
+
res.should == 1
|
131
|
+
end
|
132
|
+
|
133
|
+
it "on blocks which return falsey cause evaulation to continue" do
|
134
|
+
res = switch do
|
135
|
+
on { nil }
|
136
|
+
on { :turtle }
|
137
|
+
end
|
138
|
+
res.should == :turtle
|
139
|
+
end
|
140
|
+
|
141
|
+
it "can execute within an object" do
|
142
|
+
test_test = proc do
|
143
|
+
switch do
|
144
|
+
on(count.length > 5) { count }
|
145
|
+
on(count.length < 5) { count }
|
146
|
+
on(count.length == 5) { :five }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
@test.instance_eval(&test_test).should == :five
|
151
|
+
@test.count.slice!(0,3)
|
152
|
+
@test.instance_eval(&test_test).should == [4,5]
|
153
|
+
@test.count.concat [1,1,1,1]
|
154
|
+
@test.instance_eval(&test_test).should == [4,5,1,1,1,1]
|
155
|
+
end
|
156
|
+
|
157
|
+
it "can look in an object if it's not within it" do
|
158
|
+
res = switch do
|
159
|
+
on(@test.flavors :lime)
|
160
|
+
on(@test.flavors :snoopy)
|
161
|
+
on(@test.flavors :mushrooms)
|
162
|
+
end
|
163
|
+
res.should == "pizza"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "can't call on from outside of switch block" do
|
167
|
+
lambda { on }.should raise_error NameError
|
168
|
+
|
169
|
+
# ensure the on method is defined the next time
|
170
|
+
# we call it
|
171
|
+
flag = false
|
172
|
+
t1 = Thread.new do
|
173
|
+
switch do
|
174
|
+
on false
|
175
|
+
flag = true
|
176
|
+
sleep 0.1
|
177
|
+
on true
|
178
|
+
end
|
179
|
+
flag = false
|
180
|
+
end
|
181
|
+
|
182
|
+
Timeout.timeout(1) { loop until flag == true }
|
183
|
+
|
184
|
+
# should transform the uncaught :throw error into a NameError
|
185
|
+
(self.respond_to? :on).should be true #it's defined in the thread
|
186
|
+
lambda { on }.should raise_error NameError, "on without associated switch"
|
187
|
+
|
188
|
+
loop until flag == false
|
189
|
+
(self.respond_to? :on).should be false
|
190
|
+
lambda { on }.should raise_error NameError
|
191
|
+
end
|
192
|
+
|
193
|
+
it "can raise past the (internal) throw" do
|
194
|
+
lambda do
|
195
|
+
switch do
|
196
|
+
on { |a,b,c| raise ArgumentError }
|
197
|
+
end
|
198
|
+
end.should raise_error ArgumentError
|
199
|
+
end
|
200
|
+
|
201
|
+
it "can nest safely" do
|
202
|
+
res = switch do
|
203
|
+
on(@test.compliment =~ /mustache/) do |it|
|
204
|
+
switch do
|
205
|
+
on(@test.compliment =~ /baron/) { "The Barons Mustache"}
|
206
|
+
on(@test.compliment =~ /sheriff/) { "The Sheriff's Mustache" }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
on(@test.count == 5)
|
210
|
+
end
|
211
|
+
res.should == "The Sheriff's Mustache"
|
212
|
+
|
213
|
+
res = switch do
|
214
|
+
on do |it|
|
215
|
+
switch do
|
216
|
+
on { nil }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
on { 5 }
|
220
|
+
end
|
221
|
+
res.should == 5
|
222
|
+
end
|
223
|
+
|
224
|
+
it "can be called with explicit receiver" do
|
225
|
+
# this makes it act a little bit like "tap"
|
226
|
+
res = @test.switch do
|
227
|
+
on (flavors :lime) { |it| it.blah } #would raise
|
228
|
+
on (flavors :snoopy)
|
229
|
+
on { raise 'terribly awry'}
|
230
|
+
end
|
231
|
+
|
232
|
+
res.should == "pizza"
|
233
|
+
end
|
234
|
+
|
235
|
+
it "can act like a regular case statement if called with a parameter" do
|
236
|
+
test = lambda do |test_obj|
|
237
|
+
res = switch test_obj do
|
238
|
+
on(/baron/) { |it| "'#{it.chomp('.')},' said the queen."}
|
239
|
+
on(/sheriff/) { |it| "'#{it.chomp('.')},' I said."}
|
240
|
+
on
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
test.call("That's a nice mustache sheriff.").should == "'That's a nice mustache sheriff,' I said."
|
245
|
+
test.call("That's a nice mustache baron.").should == "'That's a nice mustache baron,' said the queen."
|
246
|
+
test.call("I hate mustache's").should == "I hate mustache's"
|
247
|
+
end
|
248
|
+
|
249
|
+
it "can act like a regular case statement nested" do
|
250
|
+
remember = @test.compliment.dup
|
251
|
+
res = switch @test.compliment do
|
252
|
+
on /sheriff/ do |it|
|
253
|
+
# its is @test.compliment
|
254
|
+
switch it do
|
255
|
+
#it is STILL @test.compliment
|
256
|
+
on(/mustache/) { |thing| false }
|
257
|
+
#return false to fall through
|
258
|
+
end
|
259
|
+
end
|
260
|
+
# here it should STILL be @test.compliment
|
261
|
+
# which means this should NOT match
|
262
|
+
on(/baron/) { |it| it.blah }
|
263
|
+
# but this should.
|
264
|
+
on(/sheriff/) { |it| it + "okay" }
|
265
|
+
end
|
266
|
+
res.should == remember + "okay"
|
267
|
+
end
|
268
|
+
|
269
|
+
it "can be called both with and without a parameter" do
|
270
|
+
res = switch do
|
271
|
+
# there is no default __it__
|
272
|
+
on @test.compliment =~ /sheriff/ do |it|
|
273
|
+
# it here is "43" the place where sheriff occures
|
274
|
+
switch it do |ot|
|
275
|
+
on(ot > 43) { raise 'ohnoes' }
|
276
|
+
on(ot < 43) { raise 'ohnoes again'}
|
277
|
+
# this will signal completion
|
278
|
+
on(ot == 43) { 'okay!' }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# so this will not run
|
282
|
+
on(@test.compliment =~ /baron/) { |it| it.blah }
|
283
|
+
end
|
284
|
+
|
285
|
+
res.should == 'okay!'
|
286
|
+
|
287
|
+
res = switch @test.compliment do
|
288
|
+
# this will match
|
289
|
+
on /mustache/ do |it|
|
290
|
+
switch do
|
291
|
+
# this should not match, but it's truthy
|
292
|
+
on /poor/
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
#so the result of the entire block is the last truthy thing
|
298
|
+
res.should == /poor/
|
299
|
+
|
300
|
+
res = switch @test.compliment do
|
301
|
+
on /mustache/ do |it|
|
302
|
+
# this will CLOSE around it.
|
303
|
+
switch do
|
304
|
+
on(it =~ /sheriff/) { 'yes it is' }
|
305
|
+
on true
|
306
|
+
end
|
307
|
+
end
|
308
|
+
on(/baron/) { true }
|
309
|
+
end
|
310
|
+
res.should == 'yes it is'
|
311
|
+
|
312
|
+
|
313
|
+
# can switch eval contexts each time
|
314
|
+
|
315
|
+
res = switch "this" do
|
316
|
+
on /this/ do
|
317
|
+
switch "that" do
|
318
|
+
on /that/ do
|
319
|
+
switch "thing" do
|
320
|
+
on /thing/ do
|
321
|
+
"foogle bear"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
res.should == "foogle bear"
|
330
|
+
end
|
331
|
+
|
332
|
+
it "nests in threads" do
|
333
|
+
res1, res2 = nil,nil
|
334
|
+
|
335
|
+
t1 = Thread.new do
|
336
|
+
res1 = switch "this" do
|
337
|
+
on /this/ do
|
338
|
+
switch "that" do
|
339
|
+
on(/that/) { "that" }
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
t2 = Thread.new do
|
346
|
+
res2 = switch "weasel" do
|
347
|
+
on /weasel/ do
|
348
|
+
switch "monkey" do
|
349
|
+
on(/monkey/) { "monkey" }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
Timeout.timeout 1 do
|
356
|
+
loop do
|
357
|
+
t = [t1.status, t2.status]
|
358
|
+
t.delete(false)
|
359
|
+
break if t.empty?
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
res1.should == "that"
|
364
|
+
res2.should == "monkey"
|
365
|
+
end
|
366
|
+
|
367
|
+
it "reusing blocks with params works across threads" do
|
368
|
+
thing = proc do
|
369
|
+
on(/Pestilence/) { |it| sleep 0.1; "#{it} made you cough." } #yep, mess it up
|
370
|
+
on(/Death/) { |it| "#{it} killed you." }
|
371
|
+
on(/Famine/) { |it| "#{it} made you hungry."}
|
372
|
+
on(/War/) { |it| "#{it} was loud." }
|
373
|
+
end
|
374
|
+
|
375
|
+
res1 = switch "War in Iraq", &thing
|
376
|
+
res1.should == "War in Iraq was loud."
|
377
|
+
|
378
|
+
res2, res3,res4 = nil, nil, nil
|
379
|
+
|
380
|
+
t1 = Thread.new do
|
381
|
+
res2 = switch "Famine in Ireland", &thing
|
382
|
+
end
|
383
|
+
|
384
|
+
t2 = Thread.new do
|
385
|
+
res3 = switch "Death at a Funeral", &thing
|
386
|
+
end
|
387
|
+
|
388
|
+
t3 = Thread.new do
|
389
|
+
res4 = switch "Pestilence at the presschool", &thing
|
390
|
+
end
|
391
|
+
|
392
|
+
Timeout.timeout 1 do
|
393
|
+
loop do
|
394
|
+
t = [t1.status, t2.status, t3.status]
|
395
|
+
t.delete(false)
|
396
|
+
break if t.empty?
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
res2.should == "Famine in Ireland made you hungry."
|
401
|
+
res3.should == "Death at a Funeral killed you."
|
402
|
+
res4.should == "Pestilence at the presschool made you cough."
|
403
|
+
end
|
404
|
+
|
405
|
+
it "threads?" do
|
406
|
+
res1, res2, res3 = nil, nil, nil
|
407
|
+
|
408
|
+
t1 = Thread.new do
|
409
|
+
res1 = switch do
|
410
|
+
on(@test.flavors :lime)
|
411
|
+
on(@test.flavors :snoopy)
|
412
|
+
on(@test.flavors :bacon)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
t2 = Thread.new do
|
417
|
+
res2 = switch do
|
418
|
+
on(@test.count.length == 6)
|
419
|
+
on(@test.count.length == 5) { @test.count }
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# this is designed to make sure it running after the first two
|
424
|
+
# finish. if switch isn't thread safe, the last
|
425
|
+
# call to on will raise NoMethodError
|
426
|
+
t3 = Thread.new do
|
427
|
+
res3 = switch do
|
428
|
+
on false
|
429
|
+
on nil
|
430
|
+
sleep 0.1
|
431
|
+
on true
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
Timeout.timeout 1 do
|
436
|
+
loop do
|
437
|
+
t = [t1.status, t2.status, t3.status]
|
438
|
+
t.delete(false)
|
439
|
+
break if t.empty?
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
res1.should == "pizza"
|
444
|
+
res2.should == [1,2,3,4,5]
|
445
|
+
res3.should == true
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
|
451
|
+
|
452
|
+
|
453
|
+
|
454
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
if ENV["REPORT"] == "1" then
|
2
|
+
require 'simplecov'
|
3
|
+
require 'ruby-prof'
|
4
|
+
require 'rspec-prof'
|
5
|
+
require 'ruby-debug'
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter "/spec.rb/"
|
9
|
+
coverage_dir "report/coverage"
|
10
|
+
end
|
11
|
+
|
12
|
+
class RSpecProf::Profiler
|
13
|
+
def initialize options
|
14
|
+
@options = default_options.merge(options)
|
15
|
+
ENV["RUBY_PROF_MEASURE_MODE"] = options[:measure_mode]
|
16
|
+
RubyProf.figure_measure_mode
|
17
|
+
STDOUT.puts "initi"
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :old_default :default_options
|
21
|
+
def default_options
|
22
|
+
old = old_default
|
23
|
+
old.merge({
|
24
|
+
:directory => './report/profile',
|
25
|
+
:printer => RubyProf::GraphHtmlPrinter,
|
26
|
+
:stop_block => proc do |result|
|
27
|
+
#remove every Rspec method possible.
|
28
|
+
result.threads.each do |thread_id, methods|
|
29
|
+
begin
|
30
|
+
match = /RSpec.*?/
|
31
|
+
i = 0
|
32
|
+
while i < methods.size
|
33
|
+
mi = methods[i]
|
34
|
+
method_name = mi.full_name
|
35
|
+
if method_name =~ /RSpec.*?/
|
36
|
+
(i += 1; next) if mi.root?
|
37
|
+
methods.delete_at(i)
|
38
|
+
mi.eliminate!
|
39
|
+
else
|
40
|
+
i += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
})
|
48
|
+
end
|
49
|
+
|
50
|
+
def stop
|
51
|
+
file = options[:file] + ".html"
|
52
|
+
result = RubyProf.stop
|
53
|
+
printer_class = options[:printer]
|
54
|
+
|
55
|
+
with_io(file) do |out|
|
56
|
+
if @options[:stop_block].respond_to? :call
|
57
|
+
result = @options[:stop_block].call(result)
|
58
|
+
end
|
59
|
+
printer = printer_class.new(result)
|
60
|
+
printer.print(out, :print_file => options[:print_file], :min_percent => options[:min_percent])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
RSpec.configure do |config|
|
66
|
+
config.around :each do |example|
|
67
|
+
STDOUT.puts example.metadata[:description]
|
68
|
+
example.run
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
module EmptyProfile
|
73
|
+
def profile *args
|
74
|
+
yield
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
RSpec.configure do |config|
|
79
|
+
config.extend EmptyProfile
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Requires supporting files with custom matchers and macros, etc,
|
84
|
+
# in ./support/ and its subdirectories.
|
85
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anaphoric_case
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stephen Prater
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-15 00:00:00.000000000 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &4006280 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *4006280
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: simplecov
|
28
|
+
requirement: &4006070 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *4006070
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec-prof
|
39
|
+
requirement: &4005860 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *4005860
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: i18n
|
50
|
+
requirement: &4005650 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *4005650
|
59
|
+
description: ! "You have twenty or so methods, and you want to call the first one
|
60
|
+
that returns something other than nil,\n If the dog is on fire, put it out."
|
61
|
+
email:
|
62
|
+
- me@stephenprater.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- Gemfile
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- anaphoric_case.gemspec
|
72
|
+
- lib/anaphoric_case.rb
|
73
|
+
- lib/anaphoric_case/version.rb
|
74
|
+
- spec/anaphoric_case_spec.rb
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/stephenprater/anaphoric_case
|
78
|
+
licenses: []
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project: anaphoric_case
|
97
|
+
rubygems_version: 1.5.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Provides a simple anaphoric case statement (called "switch/on")
|
101
|
+
test_files:
|
102
|
+
- spec/anaphoric_case_spec.rb
|
103
|
+
- spec/spec_helper.rb
|