rails-dev-boost 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|