once-ler 0.0.2 → 0.0.3
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/README.md +15 -4
- data/lib/onceler/ambitious_helpers.rb +3 -34
- data/lib/onceler/basic_helpers.rb +50 -15
- data/lib/onceler/recorder.rb +57 -0
- metadata +2 -3
- data/lib/onceler/around_all.rb +0 -32
data/README.md
CHANGED
|
@@ -30,17 +30,25 @@ Change a slow `before` to `before(:once)` to speed it up.
|
|
|
30
30
|
|
|
31
31
|
Change a slow `let` (or `let!`) to `let_once` to speed it up.
|
|
32
32
|
|
|
33
|
+
### subject_once(...) { ... }
|
|
34
|
+
|
|
35
|
+
Change a slow `subject` (or `subject!`) to `subject_once` to speed it up.
|
|
36
|
+
|
|
33
37
|
## Ambitious usage
|
|
34
38
|
|
|
35
39
|
If you're feeling bold, you can automatically speed up all
|
|
36
|
-
`let`s/`before
|
|
40
|
+
`let`s/`before`s in an example group:
|
|
37
41
|
|
|
38
42
|
```ruby
|
|
39
43
|
describe "something" do
|
|
40
44
|
onceler!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
before
|
|
45
|
+
|
|
46
|
+
let(:foo) { ... } # behaves like let_once
|
|
47
|
+
before { ... } # behaves like before(:once)
|
|
48
|
+
|
|
49
|
+
# but if you need explict eaches, you can still do them:
|
|
50
|
+
let_each(:foo) { ... }
|
|
51
|
+
before(:each) { ... }
|
|
44
52
|
end
|
|
45
53
|
```
|
|
46
54
|
|
|
@@ -66,6 +74,9 @@ of activerecord callbacks/inserts/updates.
|
|
|
66
74
|
|
|
67
75
|
## Caveats
|
|
68
76
|
|
|
77
|
+
* If you are doing anything database-y, you need to use transactional
|
|
78
|
+
tests (either via `use_transactional_fixtures=true`, or something like
|
|
79
|
+
[database_cleaner](https://github.com/DatabaseCleaner/database_cleaner))
|
|
69
80
|
* Your once'd blocks should have no side effects other than database
|
|
70
81
|
statements, return values, and instance variables.
|
|
71
82
|
* Your return values and instance variables need to be able to handle a
|
|
@@ -1,39 +1,8 @@
|
|
|
1
1
|
module Onceler
|
|
2
2
|
module AmbitiousHelpers
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def let(name, &block)
|
|
8
|
-
let_once(name, &block)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# TODO NamedSubjectPreventSuper
|
|
12
|
-
def subject(name = nil, &block)
|
|
13
|
-
subject_once(name, &block)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# remove auto-before'ing of ! methods, since we memoize our own way
|
|
17
|
-
def let!(name, &block)
|
|
18
|
-
let(name, &block)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def subject!(name = nil, &block)
|
|
22
|
-
subject(name, &block)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# make sure we have access to subsequently added methods when
|
|
26
|
-
# recording (not just `lets'). note that this really only works
|
|
27
|
-
# for truly functional methods with no external dependencies. e.g.
|
|
28
|
-
# methods that add stubs or set instance variables will not work
|
|
29
|
-
# while recording
|
|
30
|
-
def method_added(method_name)
|
|
31
|
-
return if method_name == @current_let_once
|
|
32
|
-
onceler = onceler(:create)
|
|
33
|
-
proxy = onceler.helper_proxy ||= new
|
|
34
|
-
onceler.helper_methods[method_name] ||= Proc.new do |*args|
|
|
35
|
-
proxy.send method_name, *args
|
|
36
|
-
end
|
|
3
|
+
# make :once the default behavior for before/let/etc.
|
|
4
|
+
def once_scope?(scope)
|
|
5
|
+
super || scope.nil?
|
|
37
6
|
end
|
|
38
7
|
end
|
|
39
8
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "onceler/ambitious_helpers"
|
|
2
|
-
require "onceler/around_all"
|
|
3
2
|
require "onceler/recorder"
|
|
4
3
|
|
|
5
4
|
module Onceler
|
|
@@ -13,15 +12,15 @@ module Onceler
|
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
module ClassMethods
|
|
16
|
-
include AroundAll
|
|
17
|
-
|
|
18
15
|
def let_once(name, &block)
|
|
16
|
+
raise ArgumentError, "wrong number of arguments (0 for 1)" if name.nil?
|
|
19
17
|
raise "#let or #subject called without a block" if block.nil?
|
|
20
18
|
onceler(:create)[name] = block
|
|
21
19
|
@current_let_once = name
|
|
22
20
|
define_method(name) { onceler[name] }
|
|
23
21
|
end
|
|
24
22
|
|
|
23
|
+
# TODO NamedSubjectPreventSuper
|
|
25
24
|
def subject_once(name = nil, &block)
|
|
26
25
|
name ||= :subject
|
|
27
26
|
let_once(name, &block)
|
|
@@ -32,12 +31,35 @@ module Onceler
|
|
|
32
31
|
onceler(:create) << block
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
def
|
|
36
|
-
|
|
34
|
+
def once_scope?(scope)
|
|
35
|
+
scope == :once
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# add second scope argument to explicitly differentiate between
|
|
39
|
+
# :each / :once
|
|
40
|
+
[:let, :let!, :subject, :subject!].each do |method|
|
|
41
|
+
once_method = (method.to_s.sub(/!\z/, '') + "_once").to_sym
|
|
42
|
+
define_method(method) do |name = nil, scope = nil, &block|
|
|
43
|
+
if once_scope?(scope)
|
|
44
|
+
send once_method, name, &block
|
|
45
|
+
else
|
|
46
|
+
super name, &block
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# set up let_each, etc.
|
|
52
|
+
[:let, :let!, :subject, :subject!].each do |method|
|
|
53
|
+
each_method = method.to_s
|
|
54
|
+
bang = each_method.sub!(/!\z/, '')
|
|
55
|
+
each_method = (each_method + "_each" + (bang ? "!" : "")).to_sym
|
|
56
|
+
define_method(each_method) do |name = nil, &block|
|
|
57
|
+
send method, name, &block
|
|
58
|
+
end
|
|
37
59
|
end
|
|
38
60
|
|
|
39
61
|
def before(*args, &block)
|
|
40
|
-
if
|
|
62
|
+
if once_scope?(args.first)
|
|
41
63
|
before_once(&block)
|
|
42
64
|
else
|
|
43
65
|
super(*args, &block)
|
|
@@ -57,6 +79,20 @@ module Onceler
|
|
|
57
79
|
Recorder.new(parent_onceler)
|
|
58
80
|
end
|
|
59
81
|
|
|
82
|
+
# make sure we have access to subsequently added methods when
|
|
83
|
+
# recording (not just `lets'). note that this really only works
|
|
84
|
+
# for truly functional methods with no external dependencies. e.g.
|
|
85
|
+
# methods that add stubs or set instance variables will not work
|
|
86
|
+
# while recording
|
|
87
|
+
def method_added(method_name)
|
|
88
|
+
return if method_name == @current_let_once
|
|
89
|
+
return if !@onceler
|
|
90
|
+
proxy = onceler.helper_proxy ||= new
|
|
91
|
+
onceler.helper_methods[method_name] ||= Proc.new do |*args|
|
|
92
|
+
proxy.send method_name, *args
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
60
96
|
private
|
|
61
97
|
|
|
62
98
|
def parent_onceler
|
|
@@ -65,18 +101,17 @@ module Onceler
|
|
|
65
101
|
end
|
|
66
102
|
|
|
67
103
|
def add_onceler_hooks!
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# conns)
|
|
71
|
-
ActiveRecord::Base.transaction(requires_new: true) do
|
|
72
|
-
group.onceler.record!
|
|
73
|
-
group.run_examples
|
|
74
|
-
raise ActiveRecord::Rollback
|
|
75
|
-
end
|
|
104
|
+
prepend_before(:all) do |group|
|
|
105
|
+
group.onceler.record!
|
|
76
106
|
end
|
|
107
|
+
|
|
108
|
+
after(:all) do |group|
|
|
109
|
+
group.onceler.reset!
|
|
110
|
+
end
|
|
111
|
+
|
|
77
112
|
# only the outer-most group needs to do this
|
|
78
113
|
unless parent_onceler
|
|
79
|
-
|
|
114
|
+
before :each do
|
|
80
115
|
onceler.replay_into!(self)
|
|
81
116
|
end
|
|
82
117
|
end
|
data/lib/onceler/recorder.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "onceler/blank_tape"
|
|
2
|
+
require "active_record"
|
|
2
3
|
|
|
3
4
|
module Onceler
|
|
4
5
|
class Recorder
|
|
@@ -24,6 +25,7 @@ module Onceler
|
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def record!
|
|
28
|
+
begin_transactions!
|
|
27
29
|
@tape = @parent ? @parent.tape.copy(mixins) : BlankTape.new(mixins)
|
|
28
30
|
proxy_recordable_methods!
|
|
29
31
|
|
|
@@ -38,6 +40,10 @@ module Onceler
|
|
|
38
40
|
@data = Marshal.dump(@tape.__data)
|
|
39
41
|
end
|
|
40
42
|
|
|
43
|
+
def reset!
|
|
44
|
+
rollback_transactions!
|
|
45
|
+
end
|
|
46
|
+
|
|
41
47
|
def proxy_recordable_methods!
|
|
42
48
|
# the proxy is used to run non-recordable methods that may be called
|
|
43
49
|
# by ones are recording. since the former could in turn call more of
|
|
@@ -92,6 +98,57 @@ module Onceler
|
|
|
92
98
|
instance.instance_variable_set(key, value)
|
|
93
99
|
end
|
|
94
100
|
end
|
|
101
|
+
|
|
102
|
+
# TODO: configurable transaction fu (say, if you have multiple
|
|
103
|
+
# conns)
|
|
104
|
+
def transaction_classes
|
|
105
|
+
[ActiveRecord::Base]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def begin_transactions!
|
|
109
|
+
transaction_classes.each do |klass|
|
|
110
|
+
begin_transaction(klass.connection)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def rollback_transactions!
|
|
115
|
+
transaction_classes.each do |klass|
|
|
116
|
+
rollback_transaction(klass.connection)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
|
121
|
+
def begin_transaction(conn)
|
|
122
|
+
conn.begin_transaction requires_new: true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def rollback_transaction(conn)
|
|
126
|
+
conn.rollback_transaction
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
def begin_transaction(klass)
|
|
130
|
+
unless conn.instance_variable_get(:@_current_transaction_records)
|
|
131
|
+
conn.instance_variable_set(:@_current_transaction_records, [])
|
|
132
|
+
end
|
|
133
|
+
if conn.open_transactions == 0
|
|
134
|
+
conn.begin_db_transaction
|
|
135
|
+
else
|
|
136
|
+
conn.create_savepoint
|
|
137
|
+
end
|
|
138
|
+
conn.increment_open_transactions
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def begin_transaction(klass)
|
|
142
|
+
conn.decrement_open_transactions
|
|
143
|
+
if conn.open_transactions == 0
|
|
144
|
+
conn.rollback_db_transaction
|
|
145
|
+
conn.send :rollback_transaction_records, true
|
|
146
|
+
else
|
|
147
|
+
conn.rollback_to_savepoint
|
|
148
|
+
conn.send :rollback_transaction_records, false
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
95
152
|
end
|
|
96
153
|
|
|
97
154
|
class Recording
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: once-ler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-06-
|
|
12
|
+
date: 2014-06-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activerecord
|
|
@@ -53,7 +53,6 @@ files:
|
|
|
53
53
|
- README.md
|
|
54
54
|
- lib/once-ler.rb
|
|
55
55
|
- lib/onceler/ambitious_helpers.rb
|
|
56
|
-
- lib/onceler/around_all.rb
|
|
57
56
|
- lib/onceler/basic_helpers.rb
|
|
58
57
|
- lib/onceler/blank_tape.rb
|
|
59
58
|
- lib/onceler/configuration.rb
|
data/lib/onceler/around_all.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# adapted from https://gist.github.com/myronmarston/2005175
|
|
2
|
-
require 'delegate'
|
|
3
|
-
require 'fiber'
|
|
4
|
-
|
|
5
|
-
module Onceler
|
|
6
|
-
module AroundAll
|
|
7
|
-
class FiberAwareGroup < SimpleDelegator
|
|
8
|
-
def run_examples
|
|
9
|
-
Fiber.yield
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def to_proc
|
|
13
|
-
proc { run_examples }
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def around_all(&block)
|
|
18
|
-
fibers = []
|
|
19
|
-
prepend_before(:all) do |group|
|
|
20
|
-
fiber = Fiber.new(&block)
|
|
21
|
-
fibers << fiber
|
|
22
|
-
fiber.resume(FiberAwareGroup.new(group))
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
after(:all) do |group|
|
|
26
|
-
fiber = fibers.pop
|
|
27
|
-
fiber.resume if fiber.alive?
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|