once-ler 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -52,6 +52,47 @@ describe "something" do
52
52
  end
53
53
  ```
54
54
 
55
+ ## Configuration
56
+
57
+ once-ler adds two new `before`/`after` scopes, `:record` and `:reset`, in
58
+ case you need to implement additional logic. `:record` runs in
59
+ conjunction with once-ler's recording phase (the implicit `before :all`).
60
+ `:reset` runs in conjunction with its cleanup phase (database rollback,
61
+ implicit `after :all`). As oppose to `:all`/`:context` hooks, these ones
62
+ are inherited down, and will run before/after any nested once-ler setup/
63
+ teardown.`
64
+
65
+ They can be used globally, or in a particular group. For example:
66
+
67
+ ```ruby
68
+ Onceler.configure do |config|
69
+ config.before :record do
70
+ # reset some caching to ensure all recordings get a blank slate
71
+ end
72
+ end
73
+
74
+ describe Foo do
75
+ before(:record) { Foo.stubs(:enabled?).returns(true) }
76
+ after(:record) { Foo.unstub(:enabled?) } # can't serialize stubs
77
+ before(:each) { Foo.stubs(:enabled?).returns(true) }
78
+
79
+ context "lol" do
80
+ before :once do
81
+ # something that needs the stub to work
82
+ end
83
+ # ...
84
+ end
85
+
86
+ context "wut" do
87
+ it do
88
+ # something that needs the stub to work
89
+ end
90
+ end
91
+
92
+ # ...
93
+ end
94
+ ```
95
+
55
96
  ## How much of a speedup will I get?
56
97
 
57
98
  YMMV, it depends on how bad your `let`s/`before`s are. For example,
@@ -63,7 +104,7 @@ model specs (spec/models/a*) **reduces their runtime by 40%**.
63
104
  Any `before(:once)`/`let_once` blocks will run just once for the current
64
105
  context/describe block, before any of its examples run. Any side effects
65
106
  (ivars) and return values will be recorded, and will then be reapplied
66
- before each spec in the block runs. Once-ler uses nested transactions
107
+ before each spec in the block runs. once-ler uses nested transactions
67
108
  (savepoints) to ensure that specs don't mess with each other's database
68
109
  rows.
69
110
 
@@ -79,9 +120,10 @@ of activerecord callbacks/inserts/updates.
79
120
  statements, return values, and instance variables.
80
121
  * Your return values and instance variables:
81
122
  1. need to be able to handle a `Marshal.dump`/`load` round trip.
82
- 1. should implement `#==` and `#hash`. for built-ins types (e.g. String)
83
- or models, this isn't a problem, but if it's a custom class you might
84
- need to add them.
123
+ RSpec mocks/doubles can't.
124
+ 1. should implement `#==`. For built-ins types (e.g. String) or models,
125
+ this isn't a problem, but if it's a custom class you might need to
126
+ go override `Object#==`
85
127
  * Your once'd blocks' behavior should not depend on side effects of other
86
128
  non-once'd blocks. For example:
87
129
  * a `before(:once)` block should not reference instance variables set by a
@@ -1,8 +1,8 @@
1
1
  module Onceler
2
2
  module AmbitiousHelpers
3
3
  # make :once the default behavior for before/let/etc.
4
- def once_scope?(scope)
5
- super || scope.nil?
4
+ def once_scopes
5
+ [:once, nil]
6
6
  end
7
7
  end
8
8
  end
@@ -31,8 +31,8 @@ module Onceler
31
31
  onceler(:create) << block
32
32
  end
33
33
 
34
- def once_scope?(scope)
35
- scope == :once
34
+ def once_scopes
35
+ [:once]
36
36
  end
37
37
 
38
38
  # add second scope argument to explicitly differentiate between
@@ -40,7 +40,7 @@ module Onceler
40
40
  [:let, :let!, :subject, :subject!].each do |method|
41
41
  once_method = (method.to_s.sub(/!\z/, '') + "_once").to_sym
42
42
  define_method(method) do |name = nil, scope = nil, &block|
43
- if once_scope?(scope)
43
+ if once_scopes.include?(scope)
44
44
  send once_method, name, &block
45
45
  else
46
46
  super name, &block
@@ -59,13 +59,27 @@ module Onceler
59
59
  end
60
60
 
61
61
  def before(*args, &block)
62
- if once_scope?(args.first)
62
+ scope = args.first
63
+ case scope
64
+ when :record, :reset
65
+ onceler(:create).hooks[:before][scope] << block
66
+ when *once_scopes
63
67
  before_once(&block)
64
68
  else
65
69
  super(*args, &block)
66
70
  end
67
71
  end
68
72
 
73
+ def after(*args, &block)
74
+ scope = args.first
75
+ case scope
76
+ when :record, :reset
77
+ onceler(:create).hooks[:after][scope] << block
78
+ else
79
+ super
80
+ end
81
+ end
82
+
69
83
  def onceler(create_own = false)
70
84
  if create_own
71
85
  @onceler ||= create_onceler!
@@ -88,7 +102,6 @@ module Onceler
88
102
 
89
103
  def add_onceler_hooks!
90
104
  before(:all) do |group|
91
- Onceler.configuration.run_callbacks(:record, :before)
92
105
  group.onceler.record!
93
106
  end
94
107
 
@@ -9,19 +9,24 @@ module Onceler
9
9
 
10
10
  class Configuration
11
11
  def before(scope, &block)
12
- callbacks[scope][:before] << block
12
+ hooks[:before][scope] << block
13
13
  end
14
14
 
15
- def callbacks
16
- @callbacks ||= Hash.new do |scopes, scope|
17
- scopes[scope] = Hash.new do |timings, timing|
18
- timings[timing] = []
19
- end
20
- end
15
+ def after(scope, &block)
16
+ hooks[:before][scope] << block
17
+ end
18
+
19
+ def hooks
20
+ @hooks ||= {
21
+ before: {record: [], reset: []},
22
+ after: {record: [], reset: []}
23
+ }
21
24
  end
22
25
 
23
- def run_callbacks(scope, timing)
24
- callbacks[scope][timing].each(&:call)
26
+ def run_hooks(timing, scope, context)
27
+ hooks[timing][scope].each do |hook|
28
+ context ? context.instance_eval(&hook) : hook.call
29
+ end
25
30
  end
26
31
  end
27
32
  end
@@ -55,6 +55,7 @@ module Onceler
55
55
  @tape.copy_from(parent.tape)
56
56
  end
57
57
 
58
+ run_before_hooks(:record, @tape)
58
59
  # we don't know the order named recordings will be called (or if
59
60
  # they'll call each other), so prep everything first
60
61
  @recordings.each do |recording|
@@ -63,19 +64,51 @@ module Onceler
63
64
  @recordings.each do |recording|
64
65
  recording.record_onto!(@tape)
65
66
  end
67
+ run_after_hooks(:record, @tape)
66
68
  @data = @tape.__data
67
69
  ensure
68
70
  Onceler.recording = false
69
71
  end
70
72
 
71
73
  def reset!
74
+ run_before_hooks(:reset)
72
75
  rollback_transactions!
76
+ run_after_hooks(:reset)
73
77
  end
74
78
 
75
79
  def parent
76
80
  @group_class.parent_onceler
77
81
  end
78
82
 
83
+ def hooks
84
+ @hooks ||= {
85
+ before: {record: [], reset: []},
86
+ after: {record: [], reset: []}
87
+ }
88
+ end
89
+
90
+ def run_before_hooks(scope, context = nil)
91
+ if parent
92
+ parent.run_before_hooks(scope, context)
93
+ else
94
+ Onceler.configuration.run_hooks(:before, scope, context)
95
+ end
96
+ hooks[:before][scope].each do |hook|
97
+ context ? context.instance_eval(&hook) : hook.call
98
+ end
99
+ end
100
+
101
+ def run_after_hooks(scope, context = nil)
102
+ hooks[:after][scope].each do |hook|
103
+ context ? context.instance_eval(&hook) : hook.call
104
+ end
105
+ if parent
106
+ parent.run_before_hooks(scope, context)
107
+ else
108
+ Onceler.configuration.run_hooks(:after, scope, context)
109
+ end
110
+ end
111
+
79
112
  def replay_into!(instance)
80
113
  @ivars, @retvals = Marshal.load(@data)
81
114
  @ivars.each do |key, value|
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.11
4
+ version: 0.0.12
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-07-02 00:00:00.000000000 Z
12
+ date: 2014-07-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord