rails-dev-boost 0.1.1
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/.gitignore +1 -0
- data/LICENSE +19 -0
- data/README.markdown +105 -0
- data/Rakefile +52 -0
- data/TODO.txt +2 -0
- data/VERSION +1 -0
- data/init.rb +3 -0
- data/lib/rails_development_boost.rb +32 -0
- data/lib/rails_development_boost/dependencies_patch.rb +332 -0
- data/lib/rails_development_boost/descendants_tracker_patch.rb +18 -0
- data/lib/rails_development_boost/loaded_file.rb +76 -0
- data/lib/rails_development_boost/reference_patch.rb +24 -0
- data/lib/rails_development_boost/view_helpers_patch.rb +17 -0
- data/rails-dev-boost.gemspec +116 -0
- data/test/constants/active_record/comment.rb +2 -0
- data/test/constants/active_record/message.rb +2 -0
- data/test/constants/active_record/other.rb +2 -0
- data/test/constants/active_record/post.rb +3 -0
- data/test/constants/deep_nesting/a.rb +2 -0
- data/test/constants/deep_nesting/a/b.rb +2 -0
- data/test/constants/deep_nesting/a/b/c.rb +2 -0
- data/test/constants/deep_nesting/a/b/c/d.rb +2 -0
- data/test/constants/double_removal/ns.rb +3 -0
- data/test/constants/double_removal/ns/c.rb +3 -0
- data/test/constants/double_removal/ns/m.rb +2 -0
- data/test/constants/mixins/client.rb +3 -0
- data/test/constants/mixins/mixin.rb +4 -0
- data/test/constants/mixins/update/mixin.rb +4 -0
- data/test/constants/nested_mixins/b.rb +2 -0
- data/test/constants/nested_mixins/b/c.rb +2 -0
- data/test/constants/nested_mixins/ma.rb +3 -0
- data/test/constants/nested_mixins/ma/mb.rb +3 -0
- data/test/constants/nested_mixins/ma/mb/mc.rb +2 -0
- data/test/constants/nested_mixins/oa.rb +4 -0
- data/test/constants/nested_mixins/oa/ob.rb +3 -0
- data/test/constants/nested_mixins/oa/ob/oc.rb +2 -0
- data/test/constants/single_removal/a.rb +2 -0
- data/test/constants/single_removal/b.rb +2 -0
- data/test/constants/singleton_mixins/a.rb +3 -0
- data/test/constants/singleton_mixins/b.rb +2 -0
- data/test/constants/subclass/a.rb +2 -0
- data/test/constants/subclass/b.rb +2 -0
- data/test/constants/subclass/c.rb +2 -0
- data/test/rails_development_boost_test.rb +222 -0
- data/test/stub_environment.rb +41 -0
- metadata +144 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module RailsDevelopmentBoost
|
2
|
+
module DescendantsTrackerPatch
|
3
|
+
def self.apply!
|
4
|
+
ActiveSupport::DescendantsTracker.extend self
|
5
|
+
ActiveSupport::DescendantsTracker.singleton_class.remove_possible_method :clear
|
6
|
+
end
|
7
|
+
|
8
|
+
def delete(klass)
|
9
|
+
class_variable_get(:@@direct_descendants).tap do |direct_descendants|
|
10
|
+
direct_descendants.delete(klass)
|
11
|
+
direct_descendants.each_value {|descendants| descendants.delete(klass)}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RailsDevelopmentBoost
|
2
|
+
class LoadedFile
|
3
|
+
@constants_to_files = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_reader :constants_to_files
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :path, :constants
|
10
|
+
delegate :constants_to_files, :to => 'self.class'
|
11
|
+
|
12
|
+
def initialize(path, constants=[])
|
13
|
+
@path = path
|
14
|
+
@constants = constants
|
15
|
+
@mtime = current_mtime
|
16
|
+
end
|
17
|
+
|
18
|
+
def changed?
|
19
|
+
previous_mtime, @mtime = @mtime, current_mtime
|
20
|
+
previous_mtime != @mtime
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_constants(new_constants)
|
24
|
+
new_constants.each do |new_constant|
|
25
|
+
(constants_to_files[new_constant] ||= []) << self
|
26
|
+
end
|
27
|
+
@constants |= new_constants
|
28
|
+
retrieve_associated_files.each {|file| file.add_constants(@constants)} if @associated_files
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_constant(const_name)
|
32
|
+
delete_from_constants_to_files(const_name)
|
33
|
+
@constants.delete(const_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def associate_with(other_loaded_file)
|
37
|
+
(@associated_files ||= []) << other_loaded_file
|
38
|
+
end
|
39
|
+
|
40
|
+
def retrieve_associated_files
|
41
|
+
associated_files, @associated_files = @associated_files, nil
|
42
|
+
associated_files
|
43
|
+
end
|
44
|
+
|
45
|
+
def require_path
|
46
|
+
@path.sub(/\.rb\Z/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.each_file_with_const(const_name, &block)
|
50
|
+
if files = constants_to_files[const_name]
|
51
|
+
files.dup.each(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def stale!
|
56
|
+
@mtime = 0
|
57
|
+
if associated_files = retrieve_associated_files
|
58
|
+
associated_files.each(&:stale!)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def delete_from_constants_to_files(const_name)
|
65
|
+
if files = constants_to_files[const_name]
|
66
|
+
files.delete(self)
|
67
|
+
constants_to_files.delete(const_name) if files.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def current_mtime
|
72
|
+
# trying to be more efficient: there is no need for a full-fledged Time instance, just grab the timestamp
|
73
|
+
File.mtime(@path).to_i rescue nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RailsDevelopmentBoost
|
2
|
+
module ReferencePatch
|
3
|
+
if defined?(ActiveSupport::Dependencies::ClassCache) # post Rails' f345e2380cac2560f3bb
|
4
|
+
def self.apply!
|
5
|
+
ActiveSupport::Dependencies::ClassCache.send :include, self
|
6
|
+
end
|
7
|
+
|
8
|
+
def loose!(const_name)
|
9
|
+
@store.delete(const_name)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
def self.apply!
|
13
|
+
ActiveSupport::Dependencies::Reference.cattr_reader :constants
|
14
|
+
ActiveSupport::Dependencies::Reference.extend ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def loose!(const_name)
|
19
|
+
constants.delete(const_name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RailsDevelopmentBoost
|
2
|
+
module ViewHelpersPatch
|
3
|
+
def self.apply!
|
4
|
+
AbstractController::Helpers::ClassMethods.send :include, self
|
5
|
+
end
|
6
|
+
|
7
|
+
# we need to explicitly associate helpers to their including controllers/mailers
|
8
|
+
def add_template_helper_with_const_association_tracking(helper_module)
|
9
|
+
ActiveSupport::Dependencies.add_explicit_dependency(helper_module, self)
|
10
|
+
add_template_helper_without_const_association_tracking(helper_module)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.alias_method_chain :add_template_helper, :const_association_tracking
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rails-dev-boost}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roman Le N\303\251grate", "thedarkone"]
|
12
|
+
s.date = %q{2010-11-11}
|
13
|
+
s.description = %q{Make your Rails app 10 times faster in development mode}
|
14
|
+
s.email = %q{roman.lenegrate@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.markdown",
|
23
|
+
"Rakefile",
|
24
|
+
"TODO.txt",
|
25
|
+
"VERSION",
|
26
|
+
"init.rb",
|
27
|
+
"lib/rails_development_boost.rb",
|
28
|
+
"lib/rails_development_boost/dependencies_patch.rb",
|
29
|
+
"lib/rails_development_boost/descendants_tracker_patch.rb",
|
30
|
+
"lib/rails_development_boost/loaded_file.rb",
|
31
|
+
"lib/rails_development_boost/reference_patch.rb",
|
32
|
+
"lib/rails_development_boost/view_helpers_patch.rb",
|
33
|
+
"rails-dev-boost.gemspec",
|
34
|
+
"test/constants/.DS_Store",
|
35
|
+
"test/constants/active_record/comment.rb",
|
36
|
+
"test/constants/active_record/message.rb",
|
37
|
+
"test/constants/active_record/other.rb",
|
38
|
+
"test/constants/active_record/post.rb",
|
39
|
+
"test/constants/deep_nesting/a.rb",
|
40
|
+
"test/constants/deep_nesting/a/b.rb",
|
41
|
+
"test/constants/deep_nesting/a/b/c.rb",
|
42
|
+
"test/constants/deep_nesting/a/b/c/d.rb",
|
43
|
+
"test/constants/double_removal/ns.rb",
|
44
|
+
"test/constants/double_removal/ns/c.rb",
|
45
|
+
"test/constants/double_removal/ns/m.rb",
|
46
|
+
"test/constants/mixins/client.rb",
|
47
|
+
"test/constants/mixins/mixin.rb",
|
48
|
+
"test/constants/mixins/update/mixin.rb",
|
49
|
+
"test/constants/nested_mixins/b.rb",
|
50
|
+
"test/constants/nested_mixins/b/c.rb",
|
51
|
+
"test/constants/nested_mixins/ma.rb",
|
52
|
+
"test/constants/nested_mixins/ma/mb.rb",
|
53
|
+
"test/constants/nested_mixins/ma/mb/mc.rb",
|
54
|
+
"test/constants/nested_mixins/oa.rb",
|
55
|
+
"test/constants/nested_mixins/oa/ob.rb",
|
56
|
+
"test/constants/nested_mixins/oa/ob/oc.rb",
|
57
|
+
"test/constants/single_removal/a.rb",
|
58
|
+
"test/constants/single_removal/b.rb",
|
59
|
+
"test/constants/singleton_mixins/a.rb",
|
60
|
+
"test/constants/singleton_mixins/b.rb",
|
61
|
+
"test/constants/subclass/a.rb",
|
62
|
+
"test/constants/subclass/b.rb",
|
63
|
+
"test/constants/subclass/c.rb",
|
64
|
+
"test/rails_development_boost_test.rb",
|
65
|
+
"test/stub_environment.rb"
|
66
|
+
]
|
67
|
+
s.homepage = %q{http://github.com/thedarkone/rails-dev-boost}
|
68
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
69
|
+
s.require_paths = ["lib"]
|
70
|
+
s.rubygems_version = %q{1.3.7}
|
71
|
+
s.summary = %q{Speeds up Rails development mode}
|
72
|
+
s.test_files = [
|
73
|
+
"test/constants/active_record/comment.rb",
|
74
|
+
"test/constants/active_record/message.rb",
|
75
|
+
"test/constants/active_record/other.rb",
|
76
|
+
"test/constants/active_record/post.rb",
|
77
|
+
"test/constants/deep_nesting/a/b/c/d.rb",
|
78
|
+
"test/constants/deep_nesting/a/b/c.rb",
|
79
|
+
"test/constants/deep_nesting/a/b.rb",
|
80
|
+
"test/constants/deep_nesting/a.rb",
|
81
|
+
"test/constants/double_removal/ns/c.rb",
|
82
|
+
"test/constants/double_removal/ns/m.rb",
|
83
|
+
"test/constants/double_removal/ns.rb",
|
84
|
+
"test/constants/mixins/client.rb",
|
85
|
+
"test/constants/mixins/mixin.rb",
|
86
|
+
"test/constants/mixins/update/mixin.rb",
|
87
|
+
"test/constants/nested_mixins/b/c.rb",
|
88
|
+
"test/constants/nested_mixins/b.rb",
|
89
|
+
"test/constants/nested_mixins/ma/mb/mc.rb",
|
90
|
+
"test/constants/nested_mixins/ma/mb.rb",
|
91
|
+
"test/constants/nested_mixins/ma.rb",
|
92
|
+
"test/constants/nested_mixins/oa/ob/oc.rb",
|
93
|
+
"test/constants/nested_mixins/oa/ob.rb",
|
94
|
+
"test/constants/nested_mixins/oa.rb",
|
95
|
+
"test/constants/single_removal/a.rb",
|
96
|
+
"test/constants/single_removal/b.rb",
|
97
|
+
"test/constants/singleton_mixins/a.rb",
|
98
|
+
"test/constants/singleton_mixins/b.rb",
|
99
|
+
"test/constants/subclass/a.rb",
|
100
|
+
"test/constants/subclass/b.rb",
|
101
|
+
"test/constants/subclass/c.rb",
|
102
|
+
"test/rails_development_boost_test.rb",
|
103
|
+
"test/stub_environment.rb"
|
104
|
+
]
|
105
|
+
|
106
|
+
if s.respond_to? :specification_version then
|
107
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
108
|
+
s.specification_version = 3
|
109
|
+
|
110
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
111
|
+
else
|
112
|
+
end
|
113
|
+
else
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'mocha'
|
3
|
+
|
4
|
+
require 'stub_environment'
|
5
|
+
require 'rails_development_boost'
|
6
|
+
RailsDevelopmentBoost.apply!
|
7
|
+
|
8
|
+
class RailsDevelopmentBoostTest < Test::Unit::TestCase
|
9
|
+
def test_single_removal
|
10
|
+
load_from "single_removal"
|
11
|
+
|
12
|
+
assert_same_object_id('A') { reload! }
|
13
|
+
assert_different_object_id('A') { reload! { update("a.rb") } }
|
14
|
+
assert_different_object_id('A') { reload! { update("a.rb") } }
|
15
|
+
assert_same_object_id('A') { reload! }
|
16
|
+
|
17
|
+
assert_same_object_id('B') do
|
18
|
+
assert_different_object_id('A') do
|
19
|
+
reload! do
|
20
|
+
update("a.rb")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_subclass_update_cascade
|
27
|
+
load_from "subclass"
|
28
|
+
|
29
|
+
assert_different_object_id 'A', 'B' do
|
30
|
+
assert_same_object_id 'C' do
|
31
|
+
reload! do
|
32
|
+
update("a.rb")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_nested_constant_update_cascade
|
39
|
+
load_from "deep_nesting"
|
40
|
+
|
41
|
+
assert_different_object_id 'A::B::C::D', 'A::B::C', 'A::B', 'A' do
|
42
|
+
reload! do
|
43
|
+
update("a.rb")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_mixin_update_cascade
|
49
|
+
load_from "mixins"
|
50
|
+
|
51
|
+
assert_different_object_id 'Mixin', 'Client' do
|
52
|
+
reload! do
|
53
|
+
update("mixin.rb")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
assert Client.public_method_defined?('from_mixin') # sanity check
|
58
|
+
|
59
|
+
# Simulate a change in the mixin file
|
60
|
+
reload! do
|
61
|
+
update("mixin.rb")
|
62
|
+
end
|
63
|
+
Deps.load_paths.unshift("#{@constant_dir}/update")
|
64
|
+
|
65
|
+
assert !Client.public_method_defined?('from_mixin')
|
66
|
+
assert Client.public_method_defined?('from_mixin_update')
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_prevention_of_removal_cycle
|
70
|
+
load_from "double_removal"
|
71
|
+
|
72
|
+
# Failure of this test = SystemStackError: stack level too deep
|
73
|
+
assert_different_object_id 'Ns::M', 'Ns::C', 'Ns' do
|
74
|
+
reload! do
|
75
|
+
update("ns/m.rb")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_nested_mixins
|
81
|
+
load_from "nested_mixins"
|
82
|
+
|
83
|
+
assert_different_object_id 'Ma::Mb::Mc', 'Ma::Mb', 'Ma' do
|
84
|
+
assert_different_object_id 'Oa::Ob::Oc', 'Oa::Ob', 'Oa' do
|
85
|
+
assert_same_object_id 'B::C' do
|
86
|
+
reload! do
|
87
|
+
update("ma/mb/mc.rb")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_singleton_mixins
|
95
|
+
load_from "singleton_mixins"
|
96
|
+
|
97
|
+
assert_different_object_id 'A' do
|
98
|
+
reload! do
|
99
|
+
update("b.rb")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
assert_same_object_id 'B' do
|
103
|
+
reload! do
|
104
|
+
update("a.rb")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_consistency_of_activerecord_registry
|
110
|
+
load_from "active_record"
|
111
|
+
|
112
|
+
fetch_registered_ar_subclasses = lambda do
|
113
|
+
ActiveRecord::Base.instance_eval { subclasses }.sort_by(&:name)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Load initial version of the models
|
117
|
+
assert_equal [Comment, Message, Other, Post], fetch_registered_ar_subclasses[]
|
118
|
+
|
119
|
+
# AR::Base subclass tree is updated
|
120
|
+
assert_different_object_id 'Message', 'Post', 'Comment' do
|
121
|
+
assert_same_object_id 'Other' do
|
122
|
+
reload! do
|
123
|
+
update("message.rb")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
assert_equal [Comment, Message, Other, Post], fetch_registered_ar_subclasses[]
|
128
|
+
|
129
|
+
# Create initial references to reflection classes
|
130
|
+
assert_equal Comment, Post.new.comments.new.class
|
131
|
+
|
132
|
+
# Reflections are updated
|
133
|
+
assert_same_object_id 'Post' do
|
134
|
+
assert_different_object_id 'Comment' do
|
135
|
+
reload! do
|
136
|
+
update("comment.rb")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
assert_equal Comment, Post.new.comments.new.class
|
141
|
+
end
|
142
|
+
|
143
|
+
protected
|
144
|
+
|
145
|
+
CONSTANT_DIR = "#{File.dirname(__FILE__)}/constants".freeze
|
146
|
+
CONSTANT_FILES = Dir.chdir(CONSTANT_DIR) { Dir.glob("**/*.rb") }.freeze
|
147
|
+
|
148
|
+
Deps = ActiveSupport::Dependencies
|
149
|
+
|
150
|
+
def setup
|
151
|
+
# Cleanup
|
152
|
+
clean_up! "setup"
|
153
|
+
@constant_dir = CONSTANT_DIR
|
154
|
+
|
155
|
+
# Configuration
|
156
|
+
Deps.load_paths = [CONSTANT_DIR]
|
157
|
+
Deps.logger = Logger.new(STDERR)
|
158
|
+
Deps.log_activity = false
|
159
|
+
|
160
|
+
# Stub mtimes
|
161
|
+
CONSTANT_FILES.each { |file| stub_mtime(file) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def teardown
|
165
|
+
clean_up! "teardown"
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def load_from(root)
|
171
|
+
@constant_dir = "#{CONSTANT_DIR}/#{root}"
|
172
|
+
Deps.load_paths = [@constant_dir]
|
173
|
+
end
|
174
|
+
|
175
|
+
def update(path)
|
176
|
+
stub_mtime(path, File.mtime("#{@constant_dir}/#{path}") + 1)
|
177
|
+
end
|
178
|
+
|
179
|
+
def stub_mtime(path, time=1)
|
180
|
+
File.stubs(:mtime).with("#{@constant_dir}/#{path}").returns time
|
181
|
+
end
|
182
|
+
|
183
|
+
def reload!
|
184
|
+
ActionController::Dispatcher.new.cleanup_application
|
185
|
+
yield if block_given?
|
186
|
+
ActionController::Dispatcher.new.reload_application
|
187
|
+
end
|
188
|
+
|
189
|
+
def clean_up!(stage)
|
190
|
+
message = "#{stage} dependency cleanup of <#{@method_name}> failed"
|
191
|
+
|
192
|
+
Deps.clear
|
193
|
+
Deps.history.clear
|
194
|
+
|
195
|
+
assert_equal([], Deps.constants_being_removed, message)
|
196
|
+
assert_equal([], Deps.module_cache, message)
|
197
|
+
assert_equal(Set.new, Deps.loaded, message)
|
198
|
+
assert_equal({}, Deps.file_map, message)
|
199
|
+
assert_equal([], Deps.autoloaded_constants, message)
|
200
|
+
end
|
201
|
+
|
202
|
+
def assert_same_object_id(*expressions, &block)
|
203
|
+
each_object_id_diff(block, expressions) do |expr, before, after|
|
204
|
+
assert_equal(before, after, "<#{expr}.object_id> has changed")
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def assert_different_object_id(*expressions, &block)
|
209
|
+
each_object_id_diff(block, expressions) do |expr, before, after|
|
210
|
+
assert_not_equal(before, after, "<#{expr}.object_id> has remained the same")
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def each_object_id_diff(alter, expressions)
|
215
|
+
ids_before = expressions.map { |expr| [expr, eval("#{expr}.object_id")] }
|
216
|
+
alter.call
|
217
|
+
ids_before.each do |expr, before|
|
218
|
+
after = eval("#{expr}.object_id")
|
219
|
+
yield expr, before, after
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|