once-ler 0.0.10 → 0.0.11

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