once-ler 0.0.11 → 0.0.12

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 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