alf-sequel 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +38 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +12 -0
  6. data/README.md +11 -0
  7. data/Rakefile +11 -0
  8. data/lib/alf/sequel/adapter.rb +57 -0
  9. data/lib/alf/sequel/cog.rb +92 -0
  10. data/lib/alf/sequel/compiler/predicate.rb +76 -0
  11. data/lib/alf/sequel/compiler.rb +168 -0
  12. data/lib/alf/sequel/connection/connection_methods.rb +46 -0
  13. data/lib/alf/sequel/connection/schema_methods.rb +76 -0
  14. data/lib/alf/sequel/connection/update_methods.rb +63 -0
  15. data/lib/alf/sequel/connection.rb +23 -0
  16. data/lib/alf/sequel/loader.rb +2 -0
  17. data/lib/alf/sequel/unit_of_work/atomic.rb +35 -0
  18. data/lib/alf/sequel/unit_of_work/delete.rb +24 -0
  19. data/lib/alf/sequel/unit_of_work/insert.rb +60 -0
  20. data/lib/alf/sequel/unit_of_work/update.rb +60 -0
  21. data/lib/alf/sequel/unit_of_work.rb +11 -0
  22. data/lib/alf/sequel/version.rb +16 -0
  23. data/lib/alf/sequel.rb +7 -0
  24. data/lib/alf-sequel.rb +1 -0
  25. data/spec/adapter/test_recognize.rb +50 -0
  26. data/spec/alf.db +0 -0
  27. data/spec/compiler/test_clip.rb +40 -0
  28. data/spec/compiler/test_compact.rb +18 -0
  29. data/spec/compiler/test_extend.rb +16 -0
  30. data/spec/compiler/test_intersect.rb +18 -0
  31. data/spec/compiler/test_join.rb +18 -0
  32. data/spec/compiler/test_leaf_operand.rb +24 -0
  33. data/spec/compiler/test_matching.rb +34 -0
  34. data/spec/compiler/test_not_matching.rb +34 -0
  35. data/spec/compiler/test_predicate.rb +141 -0
  36. data/spec/compiler/test_project.rb +64 -0
  37. data/spec/compiler/test_rename.rb +26 -0
  38. data/spec/compiler/test_restrict.rb +48 -0
  39. data/spec/compiler/test_sort.rb +18 -0
  40. data/spec/compiler/test_union.rb +18 -0
  41. data/spec/compiler_helper.rb +34 -0
  42. data/spec/connection/test_connection_uri.rb +54 -0
  43. data/spec/connection/test_delete.rb +20 -0
  44. data/spec/connection/test_heading.rb +16 -0
  45. data/spec/connection/test_insert.rb +24 -0
  46. data/spec/connection/test_keys.rb +24 -0
  47. data/spec/connection/test_lock.rb +15 -0
  48. data/spec/connection/test_ping.rb +26 -0
  49. data/spec/connection/test_relvar.rb +16 -0
  50. data/spec/connection/test_update.rb +20 -0
  51. data/spec/fixtures/sap.db +0 -0
  52. data/spec/fixtures/sap.rb +43 -0
  53. data/spec/spec_helper.rb +35 -0
  54. data/spec/test_assumptions.rb +14 -0
  55. data/spec/test_sequel.rb +10 -0
  56. data/spec/unit_of_work/atomic/test_run.rb +72 -0
  57. data/spec/unit_of_work/delete/test_delete.rb +39 -0
  58. data/spec/unit_of_work/insert/test_run.rb +71 -0
  59. data/spec/unit_of_work/update/test_run.rb +39 -0
  60. data/tasks/fixtures.rake +12 -0
  61. data/tasks/gem.rake +8 -0
  62. data/tasks/test.rake +6 -0
  63. metadata +174 -0
@@ -0,0 +1,23 @@
1
+ module Alf
2
+ module Sequel
3
+ #
4
+ # Specialization of Alf::Connection to distribute Sequel datasets
5
+ #
6
+ class Connection < Alf::Adapter::Connection
7
+
8
+ def compiler
9
+ Compiler.new
10
+ end
11
+
12
+ require_relative 'connection/schema_methods'
13
+ require_relative 'connection/connection_methods'
14
+ require_relative 'connection/update_methods'
15
+ include SchemaMethods
16
+ include ConnectionMethods
17
+ include UpdateMethods
18
+
19
+ alias_method :to_s, :connection_uri
20
+
21
+ end # class Connection
22
+ end # module Sequel
23
+ end # module Alf
@@ -0,0 +1,2 @@
1
+ require "alf-core"
2
+ require "sequel"
@@ -0,0 +1,35 @@
1
+ module Alf
2
+ module Sequel
3
+ module UnitOfWork
4
+ module Atomic
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ @ran = false
9
+ @failure = nil
10
+ end
11
+ attr_reader :connection, :failure
12
+
13
+ def ran?
14
+ @ran
15
+ end
16
+
17
+ def failed?
18
+ not(@failure.nil?)
19
+ end
20
+
21
+ def run
22
+ raise IllegalStateError, "Unit of work already ran" if ran?
23
+ _run
24
+ self
25
+ rescue => ex
26
+ @failure = ex
27
+ raise
28
+ ensure
29
+ @ran = true
30
+ end
31
+
32
+ end # module Atomic
33
+ end # module UnitOfWork
34
+ end # module Sequel
35
+ end # module Alf
@@ -0,0 +1,24 @@
1
+ module Alf
2
+ module Sequel
3
+ module UnitOfWork
4
+ class Delete
5
+ include UnitOfWork::Atomic
6
+
7
+ def initialize(connection, relvar_name, predicate)
8
+ super(connection)
9
+ @relvar_name = relvar_name
10
+ @predicate = predicate
11
+ end
12
+
13
+ private
14
+
15
+ def _run
16
+ connection.with_dataset(@relvar_name, @predicate) do |d|
17
+ d.delete
18
+ end
19
+ end
20
+
21
+ end # class Delete
22
+ end # module UnitOfWork
23
+ end # module Sequel
24
+ end # module Alf
@@ -0,0 +1,60 @@
1
+ module Alf
2
+ module Sequel
3
+ module UnitOfWork
4
+ class Insert
5
+ include UnitOfWork::Atomic
6
+
7
+ def initialize(connection, relvar_name, inserted)
8
+ super(connection)
9
+ @relvar_name = relvar_name
10
+ @inserted = inserted
11
+ @insert_result = nil
12
+ end
13
+
14
+ def matching_relation
15
+ @matching_relation ||= begin
16
+ raise IllegalStateError, "Unit of work not ran" unless ran?
17
+ unless @insert_result
18
+ raise UnsupportedError, "Composite keys insertion feedback is unsupported."
19
+ end
20
+ Relation(@insert_result)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def candidate_keys
27
+ @candidate_keys ||= connection.keys(@relvar_name)
28
+ end
29
+
30
+ def primary_key
31
+ candidate_keys.first
32
+ end
33
+
34
+ def best_candidate_key
35
+ best = candidate_keys.to_a.find{|k| k.size == 1}
36
+ best || candidate_keys.first
37
+ end
38
+
39
+ def _run
40
+ connection.with_dataset(@relvar_name) do |d|
41
+ bk = best_candidate_key
42
+ if bk and bk.size == 1
43
+ pk_field_name = bk.to_a.first
44
+ supported = d.supports_returning?(:insert)
45
+ d = d.returning(pk_field_name) if supported
46
+ @insert_result = @inserted.map{|t|
47
+ res = d.insert(t.to_hash)
48
+ supported ? res.first : { pk_field_name => res }
49
+ }
50
+ else
51
+ @inserted.each{|t| d.insert(t.to_hash) }
52
+ @insert_result = nil
53
+ end
54
+ end
55
+ end
56
+
57
+ end # class Insert
58
+ end # module UnitOfWork
59
+ end # module Sequel
60
+ end # module Alf
@@ -0,0 +1,60 @@
1
+ module Alf
2
+ module Sequel
3
+ module UnitOfWork
4
+ class Update
5
+ include UnitOfWork::Atomic
6
+
7
+ def initialize(connection, relvar_name, updating, predicate)
8
+ super(connection)
9
+ @relvar_name = relvar_name
10
+ @updating = updating
11
+ @predicate = predicate
12
+ end
13
+
14
+ def matching_relation
15
+ raise IllegalStateError, "Unit of work not ran" unless ran?
16
+
17
+ # Check that updating is understandable
18
+ unless TupleLike===@updating
19
+ raise UnsupportedError, "Non-tuple update feedback is unsupported."
20
+ end
21
+ updating = @updating.to_hash
22
+ updating_keys = updating.keys
23
+
24
+ # extract all keys, and pk in particular
25
+ keys = connection.keys(@relvar_name)
26
+ pk = keys.first
27
+
28
+ # Strategy 1), @updating contains a key
29
+ if key = keys.to_a.find{|k| !(k & updating_keys).empty? }
30
+ return Relation(@updating.project(key).to_hash)
31
+ end
32
+
33
+ raise UnsupportedError, "Unable to extract update matching relation"
34
+ end
35
+
36
+ def pk_matching_relation
37
+ mr, pkey = matching_relation, connection.keys(@relvar_name).first
38
+ if mr.to_attr_list == pkey.to_attr_list
39
+ mr
40
+ else
41
+ filter = mr.tuple_extract.to_hash
42
+ tuples = connection.cog(@relvar_name)
43
+ .filter(filter)
44
+ .select(pkey.to_a)
45
+ Relation(tuples)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def _run
52
+ connection.with_dataset(@relvar_name, @predicate) do |d|
53
+ d.update Tuple(@updating).to_hash
54
+ end
55
+ end
56
+
57
+ end # class Update
58
+ end # module UnitOfWork
59
+ end # module Sequel
60
+ end # module Alf
@@ -0,0 +1,11 @@
1
+ module Alf
2
+ module Sequel
3
+ module UnitOfWork
4
+
5
+ end # module UnitOfWork
6
+ end # module Sequel
7
+ end # module Alf
8
+ require_relative 'unit_of_work/atomic'
9
+ require_relative 'unit_of_work/insert'
10
+ require_relative 'unit_of_work/delete'
11
+ require_relative 'unit_of_work/update'
@@ -0,0 +1,16 @@
1
+ module Alf
2
+ module Sequel
3
+ module Version
4
+
5
+ MAJOR = 0
6
+ MINOR = 13
7
+ TINY = 0
8
+
9
+ def self.to_s
10
+ [ MAJOR, MINOR, TINY ].join('.')
11
+ end
12
+
13
+ end
14
+ VERSION = Version.to_s
15
+ end
16
+ end
data/lib/alf/sequel.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'sequel/version'
2
+ require_relative 'sequel/loader'
3
+ require_relative 'sequel/adapter'
4
+ require_relative 'sequel/connection'
5
+ require_relative 'sequel/compiler'
6
+ require_relative 'sequel/cog'
7
+ require_relative 'sequel/unit_of_work'
data/lib/alf-sequel.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative "alf/sequel"
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Adapter, "recognizes?" do
5
+
6
+ it "recognizes ::Sequel::Database objects" do
7
+ begin
8
+ db = ::Sequel.connect(sequel_database_memory)
9
+ Adapter.recognizes?(db).should be_true
10
+ ensure
11
+ db.disconnect if db
12
+ end
13
+ end
14
+
15
+ it "recognizes sqlite files" do
16
+ Adapter.recognizes?("#{sequel_database_path}").should be_true
17
+ end
18
+
19
+ it "recognizes in memory sqlite databases" do
20
+ Adapter.recognizes?(sequel_database_memory).should be_true
21
+ end
22
+
23
+ it "recognizes a Path to a sqlite databases" do
24
+ Adapter.recognizes?(sequel_database_path).should be_true
25
+ Adapter.recognizes?("nosuchone.db").should be_true
26
+ end
27
+
28
+ it "recognizes database uris" do
29
+ Adapter.recognizes?("postgres://localhost/database").should be_true
30
+ Adapter.recognizes?(sequel_database_uri).should be_true
31
+ end
32
+
33
+ it "recognizes a Hash ala Rails" do
34
+ config = {"adapter" => "sqlite", "database" => "#{sequel_database_path}"}
35
+ Adapter.recognizes?(config).should be_true
36
+ Adapter.recognizes?(Tools.symbolize_keys(config)).should be_true
37
+ end
38
+
39
+ it "should not be too permissive" do
40
+ Adapter.recognizes?(nil).should be_false
41
+ end
42
+
43
+ it "should let Adapter autodetect sqlite files" do
44
+ Alf::Adapter.autodetect(sequel_database_path).should be(Adapter)
45
+ Alf::Adapter.autodetect("#{sequel_database_path}").should be(Adapter)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
data/spec/alf.db ADDED
Binary file
@@ -0,0 +1,40 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "clip" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when the operand is fully compilable' do
9
+ let(:expr){ clip(suppliers, [:sid, :name]) }
10
+
11
+ specify{
12
+ subject.sql.should eq("SELECT `t1`.`sid`, `t1`.`name` FROM `suppliers` AS 't1'")
13
+ }
14
+ end
15
+
16
+ context 'when the operand is fully compilable (distinct normally needed)' do
17
+ let(:expr){ clip(suppliers, [:city]) }
18
+
19
+ specify{
20
+ subject.sql.should eq("SELECT `t1`.`city` FROM `suppliers` AS 't1'")
21
+ }
22
+ end
23
+
24
+ context 'when the operand is fully compilable (allbut, distinct)' do
25
+ let(:expr){ clip(suppliers, [:sid, :name, :status], :allbut => true) }
26
+
27
+ specify{
28
+ subject.sql.should eq("SELECT `t1`.`city` FROM `suppliers` AS 't1'")
29
+ }
30
+ end
31
+
32
+ context 'when the operand is not compilable' do
33
+ let(:expr){ clip(an_operand, [:sid, :name]) }
34
+
35
+ it{ should be_a(Engine::Cog) }
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "compact" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when the operand is fully compilable' do
9
+ let(:expr){ compact(suppliers) }
10
+
11
+ specify{
12
+ subject.sql.should eq("SELECT DISTINCT * FROM `suppliers` AS 't1'")
13
+ }
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "extend" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'with a native predicate' do
9
+ let(:expr){ extend(suppliers, :big => lambda{}) }
10
+
11
+ it{ should be_a(Engine::SetAttr) }
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "intersect" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when the operand is fully compilable' do
9
+ let(:expr){ intersect(suppliers, supplies) }
10
+
11
+ specify do
12
+ subject.sql.should eq("SELECT * FROM (SELECT * FROM `suppliers` AS 't1' INTERSECT SELECT * FROM `supplies` AS 't2') AS 't3'")
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "join" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when the operand is fully compilable' do
9
+ let(:expr){ join(suppliers, supplies) }
10
+
11
+ specify do
12
+ subject.sql.should eq("SELECT * FROM (SELECT * FROM `suppliers` AS 't1' INNER JOIN (SELECT * FROM `supplies` AS 't2') AS 't3' USING (`sid`)) AS 't3'")
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "leaf_operand" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'on the proper context' do
9
+ let(:expr){ suppliers }
10
+
11
+ it{ should be_a(Cog) }
12
+ end
13
+
14
+ context 'on another context' do
15
+ let(:expr){ Algebra.named_operand(:suppliers, self) }
16
+
17
+ pending "support for multiple context is needed" do
18
+ it{ should_not be_a(Cog) }
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "matching" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when no common attributes' do
9
+ let(:expr){ matching(suppliers, project(supplies, [:part_id])) }
10
+
11
+ specify do
12
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE (EXISTS (SELECT `t2`.`part_id` FROM `supplies` AS 't2'))")
13
+ end
14
+ end
15
+
16
+ context 'when only one common attributes' do
17
+ let(:expr){ matching(suppliers, supplies) }
18
+
19
+ specify do
20
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE (`t1`.`sid` IN (SELECT `t2`.`sid` FROM `supplies` AS 't2'))")
21
+ end
22
+ end
23
+
24
+ context 'when many common attributes' do
25
+ let(:expr){ matching(suppliers, parts) }
26
+
27
+ specify do
28
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE (EXISTS (SELECT * FROM `parts` AS 't2' WHERE ((`t1`.`name` = `t2`.`name`) AND (`t1`.`city` = `t2`.`city`))))")
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ describe Compiler, "not_matching" do
5
+
6
+ subject{ compile(expr) }
7
+
8
+ context 'when no common attributes' do
9
+ let(:expr){ not_matching(suppliers, project(supplies, [:part_id])) }
10
+
11
+ specify do
12
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE NOT (EXISTS (SELECT `t2`.`part_id` FROM `supplies` AS 't2'))")
13
+ end
14
+ end
15
+
16
+ context 'when only one common attributes' do
17
+ let(:expr){ not_matching(suppliers, supplies) }
18
+
19
+ specify do
20
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE (`t1`.`sid` NOT IN (SELECT `t2`.`sid` FROM `supplies` AS 't2'))")
21
+ end
22
+ end
23
+
24
+ context 'when many common attributes' do
25
+ let(:expr){ not_matching(suppliers, parts) }
26
+
27
+ specify do
28
+ subject.sql.should eq("SELECT * FROM `suppliers` AS 't1' WHERE NOT (EXISTS (SELECT * FROM `parts` AS 't2' WHERE ((`t1`.`name` = `t2`.`name`) AND (`t1`.`city` = `t2`.`city`))))")
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,141 @@
1
+ require 'compiler_helper'
2
+ module Alf
3
+ module Sequel
4
+ class Compiler
5
+ describe Predicate do
6
+
7
+ let(:p) { Alf::Predicate }
8
+ let(:compiler) { Predicate.new }
9
+ let(:dataset) { sap.cog(:suppliers).dataset }
10
+ let(:compiled) { compiler.call(expr) }
11
+
12
+ subject{ dataset.literal(compiled) }
13
+
14
+ context 'tautology' do
15
+ let(:expr){ p.tautology }
16
+
17
+ it{ should eq("(1 = 1)") }
18
+ end
19
+
20
+ context 'contradiction' do
21
+ let(:expr){ p.contradiction }
22
+
23
+ it{ should eq("(1 = 0)") }
24
+ end
25
+
26
+ context 'var_ref' do
27
+ let(:expr){ p.var_ref(:x) }
28
+
29
+ it{ should eq("`x`") }
30
+ end
31
+
32
+ context 'literal' do
33
+ let(:expr){ p.literal(12) }
34
+
35
+ it{ should eq("12") }
36
+ end
37
+
38
+ context 'eq(var, literal)' do
39
+ let(:expr){ p.eq(:x, 12) }
40
+
41
+ it{ should eq("(`x` = 12)") }
42
+ end
43
+
44
+ context 'neq(var, literal)' do
45
+ let(:expr){ p.neq(:x, 12) }
46
+
47
+ it{ should eq("(`x` != 12)") }
48
+ end
49
+
50
+ context 'eq(var, nil)' do
51
+ let(:expr){ p.eq(:x, nil) }
52
+
53
+ it{ should eq("(`x` IS NULL)") }
54
+ end
55
+
56
+ context 'eq(var, var)' do
57
+ let(:expr){ p.eq(:x, :y) }
58
+
59
+ it{ should eq("(`x` = `y`)") }
60
+ end
61
+
62
+ context 'lt(var, literal)' do
63
+ let(:expr){ p.lt(:x, 2) }
64
+
65
+ it{ should eq("(`x` < 2)") }
66
+ end
67
+
68
+ context 'lte(var, literal)' do
69
+ let(:expr){ p.lte(:x, 2) }
70
+
71
+ it{ should eq("(`x` <= 2)") }
72
+ end
73
+
74
+ context 'gt(var, literal)' do
75
+ let(:expr){ p.gt(:x, 2) }
76
+
77
+ it{ should eq("(`x` > 2)") }
78
+ end
79
+
80
+ context 'gte(var, literal)' do
81
+ let(:expr){ p.gte(:x, 2) }
82
+
83
+ it{ should eq("(`x` >= 2)") }
84
+ end
85
+
86
+ context 'not(var)' do
87
+ let(:expr){ p.not(:x) }
88
+
89
+ it{ should eq("NOT `x`") }
90
+ end
91
+
92
+ context 'not(tautology)' do
93
+ let(:expr){ p.not(true) }
94
+
95
+ it{ should eq("(1 = 0)") }
96
+ end
97
+
98
+ context 'and' do
99
+ let(:expr){ p.eq(:x, 2) & p.eq(:y, 3) }
100
+
101
+ it{ should eq("((`x` = 2) AND (`y` = 3))") }
102
+ end
103
+
104
+ context 'or' do
105
+ let(:expr){ p.eq(:x, 2) | p.eq(:y, 3) }
106
+
107
+ it{ should eq("((`x` = 2) OR (`y` = 3))") }
108
+ end
109
+
110
+ context 'comp' do
111
+ let(:expr){ p.comp(:lt, :x => 2, :y => 3) }
112
+
113
+ it{ should eq("((`x` < 2) AND (`y` < 3))") }
114
+ end
115
+
116
+ context 'in' do
117
+ let(:expr){ p.in(:x, [2, 3]) }
118
+
119
+ it{ should eq("(`x` IN (2, 3))") }
120
+ end
121
+
122
+ context 'in with a dataset at right' do
123
+ let(:expr){ p.in(:x, dataset) }
124
+
125
+ it{ should eq("(`x` IN (SELECT * FROM `suppliers`))") }
126
+ end
127
+
128
+ context 'native' do
129
+ let(:expr){ p.native(lambda{}) }
130
+
131
+ specify{
132
+ lambda{
133
+ subject
134
+ }.should throw_symbol(:pass)
135
+ }
136
+ end
137
+
138
+ end
139
+ end
140
+ end
141
+ end