once-ler 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -52,14 +52,6 @@ describe "something" do
52
52
  end
53
53
  ```
54
54
 
55
- Or even more ambitiously, apply it to all your specs:
56
-
57
- ```ruby
58
- RSpec.configure do |c|
59
- c.onceler!
60
- end
61
- ```
62
-
63
55
  ## How much of a speedup will I get?
64
56
 
65
57
  YMMV, it depends on how bad your `let`s/`before`s are. For example,
@@ -85,8 +77,11 @@ of activerecord callbacks/inserts/updates.
85
77
  [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner))
86
78
  * Your once'd blocks should have no side effects other than database
87
79
  statements, return values, and instance variables.
88
- * Your return values and instance variables need to be able to handle a
89
- Marshal.dump/load round trip.
80
+ * Your return values and instance variables:
81
+ 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.
90
85
  * Your once'd blocks' behavior should not depend on side effects of other
91
86
  non-once'd blocks. For example:
92
87
  * a `before(:once)` block should not reference instance variables set by a
@@ -96,27 +91,3 @@ of activerecord callbacks/inserts/updates.
96
91
  in a particular example), you should ensure they don't conflict with
97
92
  each other (e.g. unique constraint violations, or one `let_once`
98
93
  mutating the return value of another).
99
- * Some effort is made to preserve object identity, but just for instance
100
- variables and return values, e.g.:
101
-
102
- ```ruby
103
- let_once(:user) { User.new }
104
- let_once(:users) { [user] }
105
-
106
- before(:once) do
107
- @joe = user
108
- @also_joe = @joe
109
- @joe_ish = [@joe]
110
- end
111
-
112
- # within an example:
113
- # user == @joe => true
114
- # user.equal? @joe => true # yay
115
- # user == @also_joe => true
116
- # user.equal? @also_joe => true # yay
117
- # user == users[0] => true
118
- # user.equal? users[0] => false # d'oh
119
- # user == @joe_ish[0] => true
120
- # user.equal? @joe_ish[0] => false # d'oh
121
- ```
122
-
@@ -45,17 +45,44 @@ module Onceler
45
45
  # we don't include inherited stuff in __data, because we might need to
46
46
  # interleave things from an intermediate before(:each) at run time
47
47
  def __mutated?(key, val)
48
+ # top-level recorders don't inherit anything, so we always want to return true
48
49
  return true unless @__inherited_cache
49
50
  # need to do both types of comparison, i.e. it's the same object in
50
51
  # memory (not reassigned), and nothing about it has been changed
51
- return false if @__inherited_values.equal?(val)
52
- return false if @__inherited_cache[key] == val
53
- true
52
+ return true unless @__inherited_values[key].equal?(val)
53
+ return true unless __values_equal?(@__inherited_cache[key], val)
54
+ false
55
+ end
56
+
57
+ def __values_equal?(obj1, obj2)
58
+ if ActiveRecord::Base === obj1 && ActiveRecord::Base === obj2
59
+ cache_key = [obj1, obj2]
60
+ return @__comparison_cache[cache_key] if @__comparison_cache.key?(cache_key)
61
+ # so as to avoid cycles while traversing AR associations
62
+ @__comparison_cache[cache_key] = true
63
+ @__comparison_cache[cache_key] = obj1.attributes == obj2.attributes &&
64
+ __associations_equal?(obj1, obj2)
65
+ else
66
+ obj1 == obj2
67
+ end
68
+ end
69
+
70
+ # if a nested once block updates an inherited object's associations,
71
+ # we want to know about it
72
+ def __associations_equal?(obj1, obj2)
73
+ cache1 = obj1.instance_variable_get(:@association_cache)
74
+ cache2 = obj2.instance_variable_get(:@association_cache)
75
+ cache1.all? { |k, v| __values_equal?(v.target, cache2[k].target) }
54
76
  end
55
77
 
56
78
  def __data(inherit = false)
57
79
  @__data ||= {}
58
- @__data[inherit] ||= Marshal.dump([__ivars(inherit), __retvals(inherit)])
80
+ @__data[inherit] ||= begin
81
+ @__comparison_cache = {}
82
+ data = Marshal.dump([__ivars(inherit), __retvals(inherit)])
83
+ @__comparison_cache = nil
84
+ data
85
+ end
59
86
  end
60
87
 
61
88
  def copy_from(other)
@@ -72,33 +72,15 @@ module Onceler
72
72
  rollback_transactions!
73
73
  end
74
74
 
75
- def reconsitute_data!
76
- @ivars, @retvals = Marshal.load(@data)
77
- identity_map = {}
78
- reidentify!(@ivars, identity_map)
79
- reidentify!(@retvals, identity_map)
80
- end
81
-
82
- def reidentify!(hash, identity_map)
83
- hash.each do |key, value|
84
- if identity_map.key?(value)
85
- hash[key] = identity_map[value]
86
- else
87
- identity_map[value] = value
88
- end
89
- end
90
- end
91
-
92
75
  def parent
93
76
  @group_class.parent_onceler
94
77
  end
95
78
 
96
79
  def replay_into!(instance)
97
- reconsitute_data!
80
+ @ivars, @retvals = Marshal.load(@data)
98
81
  @ivars.each do |key, value|
99
82
  instance.instance_variable_set(key, value)
100
83
  end
101
- @retvals
102
84
  end
103
85
 
104
86
  # TODO: configurable transaction fu (say, if you have multiple
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.10
4
+ version: 0.0.11
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-01 00:00:00.000000000 Z
12
+ date: 2014-07-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord