vex 0.2

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.
Files changed (114) hide show
  1. data/Manifest +112 -0
  2. data/Rakefile +53 -0
  3. data/VERSION +1 -0
  4. data/config/README +2 -0
  5. data/config/dependencies.rb +10 -0
  6. data/config/gem.yml +7 -0
  7. data/init.rb +36 -0
  8. data/lib/nokogiri/nokogiri_ext.rb +96 -0
  9. data/lib/vex.rb +5 -0
  10. data/lib/vex/action_controller.rb +4 -0
  11. data/lib/vex/action_controller/verify_action.rb +97 -0
  12. data/lib/vex/action_controller/whitelisted_actions.rb +45 -0
  13. data/lib/vex/active_record.rb +3 -0
  14. data/lib/vex/active_record/__init__.rb +0 -0
  15. data/lib/vex/active_record/advisory_lock.rb +11 -0
  16. data/lib/vex/active_record/advisory_lock/mysql_adapter.rb +16 -0
  17. data/lib/vex/active_record/advisory_lock/sqlite_adapter.rb +78 -0
  18. data/lib/vex/active_record/belongs_to_many.rb +143 -0
  19. data/lib/vex/active_record/find_by_extension.rb +70 -0
  20. data/lib/vex/active_record/gem.rb +8 -0
  21. data/lib/vex/active_record/lite_table.rb +139 -0
  22. data/lib/vex/active_record/lite_view.rb +140 -0
  23. data/lib/vex/active_record/mass_load.rb +65 -0
  24. data/lib/vex/active_record/mysql_backup.rb +21 -0
  25. data/lib/vex/active_record/plugins/default_value_for/LICENSE.TXT +19 -0
  26. data/lib/vex/active_record/plugins/default_value_for/README.rdoc +421 -0
  27. data/lib/vex/active_record/plugins/default_value_for/Rakefile +6 -0
  28. data/lib/vex/active_record/plugins/default_value_for/init.rb +91 -0
  29. data/lib/vex/active_record/plugins/default_value_for/test.rb +279 -0
  30. data/lib/vex/active_record/plugins/default_value_for/test.sqlite3 +0 -0
  31. data/lib/vex/active_record/random_id.rb +56 -0
  32. data/lib/vex/active_record/resolver.rb +49 -0
  33. data/lib/vex/active_record/serialize_hash.rb +125 -0
  34. data/lib/vex/active_record/to_html.rb +53 -0
  35. data/lib/vex/active_record/validate.rb +76 -0
  36. data/lib/vex/active_record/validation_error_ext.rb +68 -0
  37. data/lib/vex/base.rb +2 -0
  38. data/lib/vex/base/app.rb +75 -0
  39. data/lib/vex/base/array/at_random.rb +17 -0
  40. data/lib/vex/base/array/cross.rb +26 -0
  41. data/lib/vex/base/array/each_batch.rb +32 -0
  42. data/lib/vex/base/array/parallel_map.rb +98 -0
  43. data/lib/vex/base/deprecation.rb +41 -0
  44. data/lib/vex/base/enumerable/deep.rb +95 -0
  45. data/lib/vex/base/enumerable/enumerable_ext.rb +59 -0
  46. data/lib/vex/base/enumerable/progress.rb +71 -0
  47. data/lib/vex/base/filesystem/fast_copy.rb +61 -0
  48. data/lib/vex/base/filesystem/grep.rb +34 -0
  49. data/lib/vex/base/filesystem/lock.rb +43 -0
  50. data/lib/vex/base/filesystem/lock.rb.test.lck +0 -0
  51. data/lib/vex/base/filesystem/lock.rb.test.pid +1 -0
  52. data/lib/vex/base/filesystem/make_dirs.rb +94 -0
  53. data/lib/vex/base/filesystem/parse_filename.rb +36 -0
  54. data/lib/vex/base/filesystem/tmp_file.rb +87 -0
  55. data/lib/vex/base/filesystem/write.rb +43 -0
  56. data/lib/vex/base/hash/compact.rb +38 -0
  57. data/lib/vex/base/hash/cross.rb +117 -0
  58. data/lib/vex/base/hash/easy_access.rb +141 -0
  59. data/lib/vex/base/hash/ensure_keys.rb +18 -0
  60. data/lib/vex/base/hash/extract.rb +71 -0
  61. data/lib/vex/base/hash/extras.rb +62 -0
  62. data/lib/vex/base/hash/inspect.rb +17 -0
  63. data/lib/vex/base/hash/simple_access_methods.rb +74 -0
  64. data/lib/vex/base/invalid_argument/invalid_argument.rb +97 -0
  65. data/lib/vex/base/local_conf.rb +35 -0
  66. data/lib/vex/base/net/http_ext.rb +227 -0
  67. data/lib/vex/base/net/socket_ext.rb +43 -0
  68. data/lib/vex/base/object/insp.rb +123 -0
  69. data/lib/vex/base/object/multiple_attributes.rb +58 -0
  70. data/lib/vex/base/object/singleton_methods.rb +23 -0
  71. data/lib/vex/base/object/with_benchmark.rb +110 -0
  72. data/lib/vex/base/range_array.rb +40 -0
  73. data/lib/vex/base/range_ext.rb +28 -0
  74. data/lib/vex/base/safe_token.rb +156 -0
  75. data/lib/vex/base/string/string_ext.rb +136 -0
  76. data/lib/vex/base/thread/deferred.rb +52 -0
  77. data/lib/vex/base/thread/sleep.rb +11 -0
  78. data/lib/vex/base/time/date_ext.rb +12 -0
  79. data/lib/vex/boot.rb +40 -0
  80. data/lib/vex/boot/array.rb +22 -0
  81. data/lib/vex/boot/blank.rb +41 -0
  82. data/lib/vex/boot/string.rb +60 -0
  83. data/migration/create_request_log.rb +28 -0
  84. data/r.rb +35 -0
  85. data/script/console +19 -0
  86. data/script/rebuild +7 -0
  87. data/tasks/echoe.rake +52 -0
  88. data/tasks/validate_db.rake +14 -0
  89. data/test/ar.rb +30 -0
  90. data/test/auto.rb +31 -0
  91. data/test/base-tests/local_conf.rb +25 -0
  92. data/test/base.rb +2 -0
  93. data/test/boot.rb +3 -0
  94. data/test/config/local.defaults.yml +4 -0
  95. data/test/config/local.yml +8 -0
  96. data/test/test.sqlite3 +0 -0
  97. data/test/test.sqlite3.Class#create.lck +0 -0
  98. data/test/test.sqlite3.Class#create.lck.lck +0 -0
  99. data/test/test.sqlite3.Class#create.lck.pid +1 -0
  100. data/test/test.sqlite3.Class#create.pid +1 -0
  101. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck +0 -0
  102. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.lck +0 -0
  103. data/test/test.sqlite3.LiteView.make.holders__view_dummy.lck.pid +1 -0
  104. data/test/test.sqlite3.LiteView.make.holders__view_dummy.pid +1 -0
  105. data/test/test.sqlite3.vex.lck +0 -0
  106. data/test/test_helper.rb +49 -0
  107. data/test/tmp/copy.dat +1 -0
  108. data/test/tmp/lock.sqlite3 +0 -0
  109. data/test/tmp/lock.sqlite3.etest.lck +0 -0
  110. data/test/tmp/lock.sqlite3.etest.pid +1 -0
  111. data/test/tmp/somedata.dat +61 -0
  112. data/vex.gemspec +49 -0
  113. data/vex.tmproj +186 -0
  114. metadata +305 -0
@@ -0,0 +1,6 @@
1
+ task :default => :test
2
+
3
+ desc "Run unit tests."
4
+ task :test do
5
+ ruby "test.rb"
6
+ end
@@ -0,0 +1,91 @@
1
+ # Copyright (c) 2008 Phusion
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module DefaultValueForPlugin
22
+ class NormalValueContainer
23
+ def initialize(value)
24
+ @value = value
25
+ end
26
+
27
+ def evaluate(instance)
28
+ return @value
29
+ end
30
+ end
31
+
32
+ class BlockValueContainer
33
+ def initialize(block)
34
+ @block = block
35
+ end
36
+
37
+ def evaluate(instance)
38
+ return @block.call(instance)
39
+ end
40
+ end
41
+
42
+ module ClassMethods
43
+ def default_value_for(attribute, value = nil, &block)
44
+ if !method_defined?(:initialize_with_defaults)
45
+ include(InstanceMethods)
46
+ alias_method_chain :initialize, :defaults
47
+ class_inheritable_accessor :_default_attribute_values
48
+ self._default_attribute_values = ActiveSupport::OrderedHash.new
49
+ end
50
+ if block_given?
51
+ container = BlockValueContainer.new(block)
52
+ else
53
+ container = NormalValueContainer.new(value)
54
+ end
55
+ _default_attribute_values[attribute.to_s] = container
56
+ end
57
+
58
+ def default_values(values)
59
+ values.each_pair do |key, value|
60
+ if value.kind_of? Proc
61
+ default_value_for(key, &value)
62
+ else
63
+ default_value_for(key, value)
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ module InstanceMethods
70
+ def initialize_with_defaults(attrs = nil)
71
+ initialize_without_defaults(attrs) do
72
+ if attrs
73
+ stringified_attrs = attrs.stringify_keys
74
+ safe_attrs = remove_attributes_protected_from_mass_assignment(stringified_attrs)
75
+ safe_attribute_names = safe_attrs.keys.map do |x|
76
+ x.to_s
77
+ end
78
+ end
79
+ self.class._default_attribute_values.each do |attribute, container|
80
+ if safe_attribute_names.nil? || safe_attribute_names.none? { |attr_name| attr_name =~ /^#{attribute}($|\()/ }
81
+ __send__("#{attribute}=", container.evaluate(self))
82
+ changed_attributes.delete(attribute)
83
+ end
84
+ end
85
+ yield(self) if block_given?
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ ActiveRecord::Base.extend(DefaultValueForPlugin::ClassMethods)
@@ -0,0 +1,279 @@
1
+ # Copyright (c) 2008, 2009 Phusion
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'rubygems'
22
+ require 'active_record'
23
+ require 'test/unit'
24
+ require File.dirname(__FILE__) + '/init'
25
+ Dir.chdir(File.dirname(__FILE__))
26
+
27
+ if RUBY_PLATFORM == "java"
28
+ database_adapter = "jdbcsqlite3"
29
+ else
30
+ database_adapter = "sqlite3"
31
+ end
32
+
33
+ File.unlink('test.sqlite3') rescue nil
34
+ ActiveRecord::Base.logger = Logger.new(STDERR)
35
+ ActiveRecord::Base.logger.level = Logger::WARN
36
+ ActiveRecord::Base.establish_connection(
37
+ :adapter => database_adapter,
38
+ :database => 'test.sqlite3'
39
+ )
40
+ ActiveRecord::Base.connection.create_table(:users, :force => true) do |t|
41
+ t.string :username
42
+ t.integer :default_number
43
+ end
44
+ ActiveRecord::Base.connection.create_table(:numbers, :force => true) do |t|
45
+ t.string :type
46
+ t.integer :number
47
+ t.integer :count, :null => false, :default => 1
48
+ t.integer :user_id
49
+ t.timestamp :timestamp
50
+ end
51
+
52
+ class User < ActiveRecord::Base
53
+ has_many :numbers, :class_name => 'TestClass'
54
+ end
55
+
56
+ class Number < ActiveRecord::Base
57
+ end
58
+
59
+ class DefaultValuePluginTest < Test::Unit::TestCase
60
+ def setup
61
+ Number.create(:number => 9876)
62
+ end
63
+
64
+ def teardown
65
+ Number.delete_all
66
+ end
67
+
68
+ def define_model_class(name = "TestClass", parent_class_name = "ActiveRecord::Base", &block)
69
+ Object.send(:remove_const, name) rescue nil
70
+ eval("class #{name} < #{parent_class_name}; end", TOPLEVEL_BINDING)
71
+ klass = eval(name, TOPLEVEL_BINDING)
72
+ klass.class_eval do
73
+ set_table_name 'numbers'
74
+ end
75
+ klass.class_eval(&block) if block_given?
76
+ end
77
+
78
+ def test_default_value_can_be_passed_as_argument
79
+ define_model_class do
80
+ default_value_for(:number, 1234)
81
+ end
82
+ object = TestClass.new
83
+ assert_equal 1234, object.number
84
+ end
85
+
86
+ def test_default_value_can_be_passed_as_block
87
+ define_model_class do
88
+ default_value_for(:number) { 1234 }
89
+ end
90
+ object = TestClass.new
91
+ assert_equal 1234, object.number
92
+ end
93
+
94
+ def test_works_with_create
95
+ define_model_class do
96
+ default_value_for :number, 1234
97
+ end
98
+ TestClass.create
99
+ assert_not_nil TestClass.find_by_number(1234)
100
+ end
101
+
102
+ def test_overwrites_db_default
103
+ define_model_class do
104
+ default_value_for :count, 1234
105
+ end
106
+ object = TestClass.new
107
+ assert_equal 1234, object.count
108
+ end
109
+
110
+ def test_doesnt_overwrite_values_provided_by_mass_assignment
111
+ define_model_class do
112
+ default_value_for :number, 1234
113
+ end
114
+ object = TestClass.new(:number => 1, :count => 2)
115
+ assert_equal 1, object.number
116
+ end
117
+
118
+ def test_doesnt_overwrite_values_provided_by_multiparameter_assignment
119
+ define_model_class do
120
+ default_value_for :timestamp, Time.mktime(2000, 1, 1)
121
+ end
122
+ timestamp = Time.mktime(2009, 1, 1)
123
+ object = TestClass.new('timestamp(1i)' => '2009', 'timestamp(2i)' => '1', 'timestamp(3i)' => '1')
124
+ assert_equal timestamp, object.timestamp
125
+ end
126
+
127
+ def test_doesnt_overwrite_values_provided_by_constructor_block
128
+ define_model_class do
129
+ default_value_for :number, 1234
130
+ end
131
+ object = TestClass.new do |x|
132
+ x.number = 1
133
+ x.count = 2
134
+ end
135
+ assert_equal 1, object.number
136
+ end
137
+
138
+ def test_doesnt_overwrite_explicitly_provided_nil_values_in_mass_assignment
139
+ define_model_class do
140
+ default_value_for :number, 1234
141
+ end
142
+ object = TestClass.new(:number => nil)
143
+ assert_nil object.number
144
+ end
145
+
146
+ def test_default_values_are_inherited
147
+ define_model_class("TestSuperClass") do
148
+ default_value_for :number, 1234
149
+ end
150
+ define_model_class("TestClass", "TestSuperClass")
151
+ object = TestClass.new
152
+ assert_equal 1234, object.number
153
+ end
154
+
155
+ def test_doesnt_set_default_on_saved_records
156
+ define_model_class do
157
+ default_value_for :number, 1234
158
+ end
159
+ assert_equal 9876, TestClass.find(:first).number
160
+ end
161
+
162
+ def test_also_works_on_attributes_that_arent_database_columns
163
+ define_model_class do
164
+ default_value_for :hello, "hi"
165
+ attr_accessor :hello
166
+ end
167
+ object = TestClass.new
168
+ assert_equal 'hi', object.hello
169
+ end
170
+
171
+ def test_constructor_ignores_forbidden_mass_assignment_attributes
172
+ define_model_class do
173
+ default_value_for :number, 1234
174
+ attr_protected :number
175
+ end
176
+ object = TestClass.new(:number => 5678, :count => 987)
177
+ assert_equal 1234, object.number
178
+ assert_equal 987, object.count
179
+ end
180
+
181
+ def test_doesnt_conflict_with_overrided_initialize_method_in_model_class
182
+ define_model_class do
183
+ def initialize(attrs = {})
184
+ @initialized = true
185
+ super(:count => 5678)
186
+ end
187
+
188
+ default_value_for :number, 1234
189
+ end
190
+ object = TestClass.new
191
+ assert_equal 1234, object.number
192
+ assert_equal 5678, object.count
193
+ assert object.instance_variable_get('@initialized')
194
+ end
195
+
196
+ def test_model_instance_is_passed_to_the_given_block
197
+ $instance = nil
198
+ define_model_class do
199
+ default_value_for :number do |n|
200
+ $instance = n
201
+ end
202
+ end
203
+ object = TestClass.new
204
+ assert_same object, $instance
205
+ end
206
+
207
+ def test_can_specify_default_value_via_association
208
+ user = User.create(:username => 'Kanako', :default_number => 123)
209
+ define_model_class do
210
+ belongs_to :user
211
+
212
+ default_value_for :number do |n|
213
+ n.user.default_number
214
+ end
215
+ end
216
+ object = user.numbers.create
217
+ assert_equal 123, object.number
218
+ end
219
+
220
+ def test_default_values
221
+ define_model_class do
222
+ default_values :type => "normal",
223
+ :number => lambda { 10 + 5 }
224
+ end
225
+
226
+ object = TestClass.new
227
+ assert_equal("normal", object.type)
228
+ assert_equal(15, object.number)
229
+ end
230
+
231
+ def test_default_value_order
232
+ define_model_class do
233
+ default_value_for :count, 5
234
+ default_value_for :number do |this|
235
+ this.count * 2
236
+ end
237
+ end
238
+ object = TestClass.new
239
+ assert_equal(5, object.count)
240
+ assert_equal(10, object.number)
241
+ end
242
+
243
+ def test_attributes_with_default_values_are_not_marked_as_changed
244
+ define_model_class do
245
+ default_value_for :count, 5
246
+ default_value_for :number, 2
247
+ end
248
+
249
+ object = TestClass.new
250
+ assert(!object.changed?)
251
+ assert_equal([], object.changed)
252
+
253
+ object.type = "foo"
254
+ assert(object.changed?)
255
+ assert_equal(["type"], object.changed)
256
+ end
257
+
258
+ def test_default_values_are_not_duplicated
259
+ define_model_class do
260
+ set_table_name "users"
261
+ default_value_for :username, "hello"
262
+ end
263
+ user1 = TestClass.new
264
+ user1.username << " world"
265
+ user2 = TestClass.new
266
+ assert_equal("hello world", user2.username)
267
+ end
268
+
269
+ def test_constructor_does_not_affect_the_hash_passed_to_it
270
+ define_model_class do
271
+ default_value_for :count, 5
272
+ end
273
+
274
+ options = { :count => 5, :user_id => 1 }
275
+ options_dup = options.dup
276
+ object = TestClass.new(options)
277
+ assert_equal(options_dup, options)
278
+ end
279
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveRecord::RandomID
2
+ module Generator
3
+ def self.string
4
+ # This puts 40 random bits into a string.
5
+ ActiveSupport::SecureRandom.random_number(0xffffffffff).to_s(36)
6
+ end
7
+
8
+ def self.integer
9
+ # This gives us 31 bits of random data.
10
+ ActiveSupport::SecureRandom.random_number 0x7fffffff
11
+ end
12
+
13
+ def self.large
14
+ # This gives us 63 bits of random data.
15
+ ActiveSupport::SecureRandom.random_number 0x7fffffffffffffff
16
+ end
17
+ end
18
+
19
+ def with_random_column(column, generator = :integer)
20
+ before_validation do |rec|
21
+ next if rec.send(column)
22
+ rec.send "#{column}=", ActiveRecord::RandomID::Generator.send(generator)
23
+ end
24
+ end
25
+
26
+ def with_random_id(generator = :integer)
27
+ with_random_column primary_key, generator
28
+ end
29
+ end
30
+
31
+ class ActiveRecord::Base
32
+ extend ActiveRecord::RandomID
33
+ end
34
+
35
+ module ActiveRecord::RandomID::Etest
36
+ class RandomBase < ActiveRecord::Base
37
+ lite_table do
38
+ text :parameters
39
+ end
40
+
41
+ with_random_id
42
+ end
43
+
44
+ def test_random_id
45
+ rb = RandomBase.create!
46
+ uids = [ rb.id ]
47
+ rb.save
48
+ uids << rb.id
49
+
50
+ rb.reload
51
+ uids << rb.id
52
+
53
+ assert_not_nil(rb.id)
54
+ assert_equal([ rb.id ], uids.uniq)
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ module ActiveRecord::Resolver
2
+ #
3
+ # resolves an object.
4
+ def resolve(obj)
5
+ case obj
6
+ when self then obj
7
+ when String then self.find(Integer(obj))
8
+ when Array then obj.map { |o| resolve(o) }
9
+ else self.find(obj)
10
+ end
11
+ end
12
+ end
13
+
14
+ ActiveRecord::Base.extend ActiveRecord::Resolver
15
+
16
+ module ActiveRecord::FindByExtension::Etest
17
+ class Data < ActiveRecord::Base
18
+ end
19
+
20
+ def setup
21
+ Data.lite_table do
22
+ string :name
23
+ string :age
24
+ end
25
+
26
+ Data.create! :name => "name", :age => 2
27
+ Data.create! :name => "name", :age => 3
28
+ Data.create! :name => "name", :age => 4
29
+ Data.create! :name => "name2", :age => 2
30
+ Data.create! :name => "name2", :age => 3
31
+ Data.create! :name => "name2", :age => 4
32
+
33
+ assert_equal(6, Data.count)
34
+ end
35
+
36
+ def teardown
37
+ Data.destroy_all
38
+ end
39
+
40
+ def test_resolve
41
+ data2 = Data.find_by :name => "name", :age => 2
42
+ data3 = Data.find_by :name => "name", :age => 3
43
+
44
+ assert_equal data2, Data.resolve(data2)
45
+ assert_equal data2, Data.resolve(data2.id)
46
+ assert_equal data2, Data.resolve(data2.id.to_s)
47
+ assert_equal [ data2, data3 ].sort_by(&:id), Data.resolve([data2.id, data3.id]).sort_by(&:id)
48
+ end
49
+ end