copyable 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +264 -0
- data/Rakefile +7 -0
- data/copyable.gemspec +28 -0
- data/lib/copyable.rb +32 -0
- data/lib/copyable/config.rb +19 -0
- data/lib/copyable/copy_registry.rb +50 -0
- data/lib/copyable/copyable_extension.rb +118 -0
- data/lib/copyable/declarations/after_copy.rb +15 -0
- data/lib/copyable/declarations/associations.rb +116 -0
- data/lib/copyable/declarations/columns.rb +34 -0
- data/lib/copyable/declarations/declaration.rb +15 -0
- data/lib/copyable/declarations/declarations.rb +14 -0
- data/lib/copyable/declarations/disable_all_callbacks_and_observers_except_validate.rb +9 -0
- data/lib/copyable/declarations/main.rb +32 -0
- data/lib/copyable/exceptions.rb +10 -0
- data/lib/copyable/model_hooks.rb +40 -0
- data/lib/copyable/option_checker.rb +23 -0
- data/lib/copyable/railtie.rb +9 -0
- data/lib/copyable/saver.rb +19 -0
- data/lib/copyable/single_copy_enforcer.rb +68 -0
- data/lib/copyable/syntax_checking/association_checker.rb +41 -0
- data/lib/copyable/syntax_checking/column_checker.rb +43 -0
- data/lib/copyable/syntax_checking/completeness_checker.rb +27 -0
- data/lib/copyable/syntax_checking/declaration_checker.rb +26 -0
- data/lib/copyable/syntax_checking/declaration_stubber.rb +18 -0
- data/lib/copyable/syntax_checking/syntax_checker.rb +15 -0
- data/lib/copyable/version.rb +3 -0
- data/lib/tasks/copyable.rake +55 -0
- data/spec/config_spec.rb +132 -0
- data/spec/copy_registry_spec.rb +55 -0
- data/spec/copyable_after_copy_spec.rb +28 -0
- data/spec/copyable_associations_spec.rb +366 -0
- data/spec/copyable_columns_spec.rb +116 -0
- data/spec/copyable_spec.rb +7 -0
- data/spec/create_copy_spec.rb +136 -0
- data/spec/deep_structure_copy_spec.rb +169 -0
- data/spec/helper/copyable_spec_helper.rb +15 -0
- data/spec/helper/test_models.rb +136 -0
- data/spec/helper/test_tables.rb +135 -0
- data/spec/model_hooks_spec.rb +66 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/stress_test_spec.rb +261 -0
- data/spec/syntax_checking/association_checker_spec.rb +80 -0
- data/spec/syntax_checking/column_checker_spec.rb +49 -0
- data/spec/syntax_checking/declaration_checker_spec.rb +58 -0
- data/spec/syntax_checking_spec.rb +258 -0
- data/spec/transaction_spec.rb +78 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4c32d813761a0de036a2ca27bc77200ef2d4a2ac
|
4
|
+
data.tar.gz: dff39390302c0407009525d15daa493abaf58665
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2943740d29f92450b8c1d62b5973459148606c9b6b05d227232bb9ddb7dcddfafd1091830590556a0105656d3dbe45c7e7fddba652992d50383a131215aecc0
|
7
|
+
data.tar.gz: f40e400af14620fa92ecf7d5b1fa783e354ed1e25d4a4726e441efedfe77df69e8ac002652883ce1f6d651b960d47662b9d196fadb9a65cad222904d77e9de63
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.idea
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Dennis Chan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
# Copyable
|
2
|
+
|
3
|
+
Copyable makes it easy to copy ActiveRecord models.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'copyable'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install copyable
|
18
|
+
|
19
|
+
# Usage
|
20
|
+
|
21
|
+
|
22
|
+
## Basic Usage
|
23
|
+
|
24
|
+
Copyable gives you a create_copy! method that you can use to copy ActiveRecord models:
|
25
|
+
|
26
|
+
isaiah = Book.create!(title: "Isaiah")
|
27
|
+
copy_of_isaiah = isaiah.create_copy!
|
28
|
+
|
29
|
+
Now there are two books called "Isaiah" in the database.
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
## The Copyable Declaration
|
34
|
+
|
35
|
+
In order for an ActiveRecord model class to have the create_copy! method defined on it, you need to add a copyable declaration:
|
36
|
+
|
37
|
+
class Book < ActiveRecord::Base
|
38
|
+
|
39
|
+
copyable do
|
40
|
+
...
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
The copyable declaration has its own DSL for describing how this model should be copied.
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
## The Columns Declaration
|
50
|
+
|
51
|
+
The columns declaration specifies how each individual column should be copied:
|
52
|
+
|
53
|
+
copyable do
|
54
|
+
...
|
55
|
+
columns({
|
56
|
+
title: :copy,
|
57
|
+
isbn: :copy,
|
58
|
+
author_id: :copy,
|
59
|
+
})
|
60
|
+
...
|
61
|
+
end
|
62
|
+
|
63
|
+
Every column *must* be listed here (with the exception of id, created_at, created_on, updated_at or updated_on).
|
64
|
+
|
65
|
+
After each column name, give advice on how to copy that column. The advice must be one of the following:
|
66
|
+
|
67
|
+
* :copy
|
68
|
+
* :do_not_copy
|
69
|
+
* lambda { |orig| ... }
|
70
|
+
|
71
|
+
:copy copies the value from the original model. :do_not_copy simply places nil in the column. Using a block lets you calculate the value of the column. The block is passed the original ActiveRecord model object that is being copied.
|
72
|
+
|
73
|
+
Here's another example:
|
74
|
+
|
75
|
+
copyable do
|
76
|
+
...
|
77
|
+
columns({
|
78
|
+
title: lambda { |orig| "Copy of #{orig.title}" },
|
79
|
+
isbn: :do_not_copy,
|
80
|
+
author_id: :copy,
|
81
|
+
})
|
82
|
+
...
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
## The Associations Declaration
|
88
|
+
|
89
|
+
The associations declaration specifies whether to copy the associated models:
|
90
|
+
|
91
|
+
copyable do
|
92
|
+
...
|
93
|
+
associations({
|
94
|
+
pages: :copy,
|
95
|
+
pictures: :copy,
|
96
|
+
readers: :do_not_copy,
|
97
|
+
})
|
98
|
+
...
|
99
|
+
end
|
100
|
+
|
101
|
+
Every association *must* be listed here, with two exceptions:
|
102
|
+
|
103
|
+
* belongs_to associations must not be listed here. Since belongs_to associations will have a foreign key column, the association will be copied when its column is copied.
|
104
|
+
* has_many :through associations must not be listed here, because they are always associated with a related has_many association that will already have been listed.
|
105
|
+
|
106
|
+
The advice must be one of the following:
|
107
|
+
|
108
|
+
* :copy
|
109
|
+
* :do_not_copy
|
110
|
+
* :copy_only_habtm_join_records
|
111
|
+
|
112
|
+
:copy will iterate through each model in the association, creating a copy. Note that the associated model class must also have a copyable declaration, so that we know how to copy it!
|
113
|
+
|
114
|
+
:do_not_copy does nothing.
|
115
|
+
|
116
|
+
:copy_only_habtm_join_records can only be used on has_and_belongs_to_many associations. In fact, you can't use :copy on has_and_belongs_to_many associations. Models associated via habtm are never actually copied, but their associations in the relevant join table can be.
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
## Callbacks
|
121
|
+
|
122
|
+
It depends on the situation as to whether you would want a particular callback to be fired when a model is copied. Since the logic of callbacks is situational, Copyable makes the decision to completely disable all callbacks and observers for the duration of the create_copy! method. The only exception are callbacks and observers related to validation.
|
123
|
+
|
124
|
+
To make it easier to reason about the code, and for the sake of being obvious, every copyable declaration *must* include a declaration called disable_all_callbacks_and_observers_except_validate. This declaration itself does not do anything; it exists as documentation.
|
125
|
+
|
126
|
+
copyable do
|
127
|
+
disable_all_callbacks_and_observers_except_validate
|
128
|
+
...
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
## The After Copy Declaration
|
134
|
+
|
135
|
+
In case you wanted to make sure a particular callback is run, or in case you had some special custom copying behavior, an after_copy declaration is provided that is called after the model has been copied. It is passed the original model and the newly copied model. Note that all callbacks and observers are disabled during the execution of the after_copy block, so you must call them explicitly if you want them to run.
|
136
|
+
|
137
|
+
copyable do
|
138
|
+
...
|
139
|
+
after_copy do |original_model, new_model|
|
140
|
+
new_model.foo = "bar"
|
141
|
+
new_model.save!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
## Putting It All Together
|
148
|
+
|
149
|
+
Here is an example of all four declarations being used in a copyable declaration. Note that all declarations are required except for after_copy.
|
150
|
+
|
151
|
+
copyable do
|
152
|
+
disable_all_callbacks_and_observers_except_validate
|
153
|
+
columns({
|
154
|
+
title: lambda { |orig| "Copy of #{orig.title}" },
|
155
|
+
isbn: :do_not_copy,
|
156
|
+
author_id: :copy,
|
157
|
+
})
|
158
|
+
associations({
|
159
|
+
pages: :copy,
|
160
|
+
pictures: :copy,
|
161
|
+
readers: :do_not_copy,
|
162
|
+
})
|
163
|
+
after_copy do |original_book, new_book|
|
164
|
+
puts "There is now a new book: #{new_book.inspect}."
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
## create_copy!
|
171
|
+
|
172
|
+
The create_copy! method allows you to override column values by passing in a hash.
|
173
|
+
|
174
|
+
isaiah = Book.create!(title: "Isaiah")
|
175
|
+
copy_of_isaiah = isaiah.create_copy!
|
176
|
+
copy_of_isaiah2 = isaiah.create_copy!(override: { title: "Foo" })
|
177
|
+
|
178
|
+
copy_of_isaiah.title will be "Copy of Isaiah" (or whatever the advice was given in the columns declaration).
|
179
|
+
|
180
|
+
copy_of_isaiah2.title will be "Foo".
|
181
|
+
|
182
|
+
Note that you pass in column names only, so if you want to update a belongs to association, you must pass in the column name, not the association name.
|
183
|
+
|
184
|
+
isaiah.create_copy!(override: { author: "Isaiah" }) # NO, bad programmer
|
185
|
+
isaiah.create_copy!(override: { author_id: 34 }) # YES
|
186
|
+
|
187
|
+
You can skip the running of validations on the copied model and its associated models. While this is not usually advisable, if you are copying a large data structure (which can take a while), you can increase the performance by skipping validations:
|
188
|
+
|
189
|
+
# turn off validations
|
190
|
+
isaiah.create_copy!(skip_validations: true)
|
191
|
+
|
192
|
+
# turn off validations and override columns
|
193
|
+
isaiah.create_copy!(override: { title: "Foo" }, skip_validations: true)
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
## Configuring
|
198
|
+
|
199
|
+
You can configure copyable's behavior by setting a configuration parameter after you've loaded copyable.
|
200
|
+
|
201
|
+
Currently copyable only has one configuration setting:
|
202
|
+
|
203
|
+
Copyable.config.suppress_schema_errors = false
|
204
|
+
|
205
|
+
This is false by default. Set this to true if you don't want Copyable to complain with a ColumnError or AssociationError if your database schema does not match your copyable declarations.
|
206
|
+
|
207
|
+
You can also set an environment variable called SUPPRESS_SCHEMA_ERRORS to true.
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
## Design Approach
|
212
|
+
|
213
|
+
***Future-proof:*** Since Copyable forces you to declare the copying behavior for each and every column and association, when you add columns or associations to a model you are forced to revisit the copying behavior. This keeps the copying logic up-to-date with the model as it grows and changes.
|
214
|
+
|
215
|
+
***Declarative:*** The declarative DSL style, although harder to debug under-the-hood, makes it much easier to work with in the model. It allows you to forget about all of the intricacies and edge cases of ActiveRecord and instead focus on describing the copying logic of your model.
|
216
|
+
|
217
|
+
***Helpful Error Messages:*** Because DSLs can be hard to debug, a concerted effort was made to provide clear error messages for user-friendly debugging.
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
## Strengths
|
222
|
+
|
223
|
+
* handles polymorphic associations
|
224
|
+
* create_copy! is run in a database transaction
|
225
|
+
* keeps track of which models have already been copied so that it does not re-copy them if it comes across them again (helpful for complex model hierarchies with redundant associations)
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
## Limitations
|
230
|
+
|
231
|
+
* not thread-safe
|
232
|
+
* copying very large data structures may use a lot of memory
|
233
|
+
* not designed with performance (CPU or database) in mind
|
234
|
+
* had to monkey-patch Rails, so only guaranteed to work with Rails 3.2 at the moment
|
235
|
+
* postponed support for single table inheritance until needed
|
236
|
+
* postponed support for keeping counter_cache correct until needed
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
## Convenience
|
241
|
+
|
242
|
+
A rake task is included that will output a basic copyable declaration given a model name. Basically, this saves you some typing.
|
243
|
+
|
244
|
+
$ rake copyable model=User
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
## Gotchas
|
249
|
+
|
250
|
+
### Creating Objects in after_copy
|
251
|
+
|
252
|
+
copyable keeps track of which models have already been copied so as not to reduplicate models if it comes across the same model through different associations. If you are creating new objects in after_copy! (such as manually copying an association instead of letting copyable do it), you do not have the benefit of copyable's checking whether the models have already been copied and may end up creating too many copies.
|
253
|
+
|
254
|
+
So the recommended approach is to use after_copy to tweak the columns of the copied record but to avoid creating new records here.
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
## Contributing
|
259
|
+
|
260
|
+
1. Fork it ( https://github.com/[my-github-username]/copyable/fork )
|
261
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
262
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
263
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
264
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/copyable.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'copyable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "copyable"
|
8
|
+
spec.version = Copyable::VERSION
|
9
|
+
spec.authors = ["Wyatt Greene", "Dennis Chan"]
|
10
|
+
spec.email = ["dchan@dmcouncil.org"]
|
11
|
+
spec.summary = %q{ActiveRecord copier}
|
12
|
+
spec.description = %q{Copyable makes it easy to copy ActiveRecord models.}
|
13
|
+
spec.homepage = "https://github.com/dmcouncil/copyable"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activerecord", "= 4.1.12"
|
22
|
+
|
23
|
+
spec.add_development_dependency "database_cleaner", "~> 1.4.0"
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "sqlite3"
|
28
|
+
end
|
data/lib/copyable.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "copyable/version"
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require_relative 'copyable/railtie' if defined?(Rails)
|
5
|
+
|
6
|
+
require_relative 'copyable/config'
|
7
|
+
require_relative 'copyable/copy_registry'
|
8
|
+
require_relative 'copyable/exceptions'
|
9
|
+
require_relative 'copyable/model_hooks'
|
10
|
+
require_relative 'copyable/option_checker'
|
11
|
+
require_relative 'copyable/saver'
|
12
|
+
require_relative 'copyable/single_copy_enforcer'
|
13
|
+
|
14
|
+
require_relative 'copyable/declarations/declaration'
|
15
|
+
require_relative 'copyable/declarations/after_copy'
|
16
|
+
require_relative 'copyable/declarations/associations'
|
17
|
+
require_relative 'copyable/declarations/columns'
|
18
|
+
require_relative 'copyable/declarations/disable_all_callbacks_and_observers_except_validate'
|
19
|
+
require_relative 'copyable/declarations/main'
|
20
|
+
require_relative 'copyable/declarations/declarations'
|
21
|
+
|
22
|
+
require_relative 'copyable/syntax_checking/declaration_stubber'
|
23
|
+
require_relative 'copyable/syntax_checking/completeness_checker'
|
24
|
+
require_relative 'copyable/syntax_checking/association_checker'
|
25
|
+
require_relative 'copyable/syntax_checking/column_checker'
|
26
|
+
require_relative 'copyable/syntax_checking/declaration_checker'
|
27
|
+
require_relative 'copyable/syntax_checking/syntax_checker'
|
28
|
+
|
29
|
+
require_relative 'copyable/copyable_extension'
|
30
|
+
|
31
|
+
# make the copyable declaration available to all ActiveRecord classes
|
32
|
+
ActiveRecord::Base.send :include, Copyable::CopyableExtension
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Allow users of copyable to alter the behavior.
|
2
|
+
|
3
|
+
module Copyable
|
4
|
+
|
5
|
+
class Config < Struct.new(:suppress_schema_errors); end
|
6
|
+
|
7
|
+
def self.config
|
8
|
+
@@config ||= Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
if ENV['SUPPRESS_SCHEMA_ERRORS'].nil?
|
14
|
+
Copyable.config.suppress_schema_errors = false
|
15
|
+
else
|
16
|
+
Copyable.config.suppress_schema_errors =
|
17
|
+
(ENV['SUPPRESS_SCHEMA_ERRORS'] == 'true' ||
|
18
|
+
ENV['SUPPRESS_SCHEMA_ERRORS'] == true)
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Refer to the comments in SingleCopyEnforcer to understand why we need
|
2
|
+
# this class.
|
3
|
+
#
|
4
|
+
# Also note that the way this class is implemented, all records being copied
|
5
|
+
# are kept in memory. For copying extremely large record trees, memory
|
6
|
+
# could be an issue, in which case this algorithm may need refactoring.
|
7
|
+
#
|
8
|
+
module Copyable
|
9
|
+
class CopyRegistry
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def register(original_record, new_record)
|
14
|
+
@registry ||= {}
|
15
|
+
key = make_hash(record: original_record)
|
16
|
+
@registry[key] = new_record
|
17
|
+
end
|
18
|
+
|
19
|
+
def already_copied?(options)
|
20
|
+
fetch_copy(options).present?
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_copy(options)
|
24
|
+
@registry ||= {}
|
25
|
+
key = make_hash(options)
|
26
|
+
@registry[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear
|
30
|
+
@registry = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def make_hash(options)
|
36
|
+
if options[:record]
|
37
|
+
id = options[:record].id
|
38
|
+
klass = options[:record].class
|
39
|
+
else
|
40
|
+
id = options[:id]
|
41
|
+
klass = options[:class]
|
42
|
+
end
|
43
|
+
raise "Record has no id" if id.nil?
|
44
|
+
"#{klass.name}-#{id}"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|