dm-adapter-simpledb 1.0.0 → 1.1.0
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/History.txt +21 -0
- data/README +21 -8
- data/Rakefile +35 -23
- data/VERSION +1 -1
- data/dm-adapter-simpledb.gemspec +44 -24
- data/lib/dm-adapter-simpledb.rb +17 -0
- data/lib/dm-adapter-simpledb/adapters/simpledb_adapter.rb +339 -0
- data/lib/dm-adapter-simpledb/chunked_string.rb +54 -0
- data/lib/dm-adapter-simpledb/migrations/simpledb_adapter.rb +45 -0
- data/lib/dm-adapter-simpledb/rake.rb +43 -0
- data/lib/dm-adapter-simpledb/record.rb +318 -0
- data/lib/{simpledb_adapter → dm-adapter-simpledb}/sdb_array.rb +0 -0
- data/lib/dm-adapter-simpledb/table.rb +40 -0
- data/lib/dm-adapter-simpledb/utils.rb +15 -0
- data/lib/simpledb_adapter.rb +2 -469
- data/scripts/simple_benchmark.rb +1 -1
- data/spec/{associations_spec.rb → integration/associations_spec.rb} +0 -0
- data/spec/{compliance_spec.rb → integration/compliance_spec.rb} +0 -0
- data/spec/{date_spec.rb → integration/date_spec.rb} +0 -0
- data/spec/{limit_and_order_spec.rb → integration/limit_and_order_spec.rb} +0 -0
- data/spec/{migrations_spec.rb → integration/migrations_spec.rb} +0 -0
- data/spec/{multiple_records_spec.rb → integration/multiple_records_spec.rb} +0 -0
- data/spec/{nils_spec.rb → integration/nils_spec.rb} +0 -0
- data/spec/{sdb_array_spec.rb → integration/sdb_array_spec.rb} +4 -5
- data/spec/{simpledb_adapter_spec.rb → integration/simpledb_adapter_spec.rb} +65 -0
- data/spec/{spec_helper.rb → integration/spec_helper.rb} +8 -3
- data/spec/unit/record_spec.rb +346 -0
- data/spec/unit/simpledb_adapter_spec.rb +80 -0
- data/spec/unit/unit_spec_helper.rb +26 -0
- metadata +58 -24
- data/tasks/devver.rake +0 -167
data/scripts/simple_benchmark.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
|
3
|
-
require
|
4
|
-
require 'spec/autorun'
|
3
|
+
require 'dm-adapter-simpledb/sdb_array'
|
5
4
|
|
6
5
|
describe 'with multiple records saved' do
|
7
6
|
|
8
7
|
class Hobbyist
|
9
8
|
include DataMapper::Resource
|
10
9
|
property :name, String, :key => true
|
11
|
-
property :hobbies,
|
10
|
+
property :hobbies, SdbArray
|
12
11
|
end
|
13
12
|
|
14
13
|
before(:each) do
|
@@ -35,7 +34,7 @@ describe 'with multiple records saved' do
|
|
35
34
|
person.save
|
36
35
|
@adapter.wait_for_consistency
|
37
36
|
lego_person = Hobbyist.first(:name => 'Jeremy Boles')
|
38
|
-
lego_person.hobbies.should == "lego"
|
37
|
+
lego_person.hobbies.should == ["lego"]
|
39
38
|
end
|
40
39
|
|
41
40
|
it 'should allow deletion of array' do
|
@@ -44,7 +43,7 @@ describe 'with multiple records saved' do
|
|
44
43
|
person.save
|
45
44
|
@adapter.wait_for_consistency
|
46
45
|
lego_person = Hobbyist.first(:name => 'Jeremy Boles')
|
47
|
-
lego_person.hobbies.should ==
|
46
|
+
lego_person.hobbies.should == []
|
48
47
|
end
|
49
48
|
|
50
49
|
it 'should find all records with diving hobby' do
|
@@ -28,6 +28,15 @@ end
|
|
28
28
|
|
29
29
|
describe DataMapper::Adapters::SimpleDBAdapter do
|
30
30
|
|
31
|
+
class Project
|
32
|
+
include DataMapper::Resource
|
33
|
+
property :id, Integer, :key => true
|
34
|
+
property :project_repo, String
|
35
|
+
property :repo_user, String
|
36
|
+
property :description, String
|
37
|
+
end
|
38
|
+
|
39
|
+
|
31
40
|
LONG_VALUE =<<-EOF
|
32
41
|
#!/bin/sh
|
33
42
|
|
@@ -159,4 +168,60 @@ EOF
|
|
159
168
|
end
|
160
169
|
end
|
161
170
|
end
|
171
|
+
|
172
|
+
context "given a pre-existing v0 record" do
|
173
|
+
before :each do
|
174
|
+
@record_name = "33d9e5a6fcbd746dc40904a6766d4166e14305fe"
|
175
|
+
record_attributes = {
|
176
|
+
"simpledb_type" => ["projects"],
|
177
|
+
"project_repo" => ["git://github.com/TwP/servolux.git"],
|
178
|
+
"files_complete" => ["nil"],
|
179
|
+
"repo_user" => ["nil"],
|
180
|
+
"id" => ["1077338529"],
|
181
|
+
"description" => [
|
182
|
+
"0002:line 2[[[NEWLINE]]]line 3[[[NEW",
|
183
|
+
"0001:line 1[[[NEWLINE]]]",
|
184
|
+
"0003:LINE]]]line 4"
|
185
|
+
]
|
186
|
+
}
|
187
|
+
@sdb.put_attributes(@domain, @record_name, record_attributes)
|
188
|
+
sleep 0.4
|
189
|
+
@record = Project.get(1077338529)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should interpret legacy nil values correctly" do
|
193
|
+
@record.repo_user.should be_nil
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should interpret legacy strings correctly" do
|
197
|
+
@record.description.should ==
|
198
|
+
"line 1\nline 2\nline 3\nline 4"
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should save legacy records without adding new metadata" do
|
202
|
+
@record.repo_user = "steve"
|
203
|
+
@record.save
|
204
|
+
sleep 0.4
|
205
|
+
attributes = @sdb.get_attributes(@domain, @record_name)[:attributes]
|
206
|
+
attributes.should_not include("__dm_metadata")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "given a brand-new record" do
|
211
|
+
before :each do
|
212
|
+
@record = Project.new(
|
213
|
+
:repo_user => "steve",
|
214
|
+
:id => 123,
|
215
|
+
:project_repo => "git://example.org/foo")
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should add metadata to the record on save" do
|
219
|
+
@record.save
|
220
|
+
sleep 0.4
|
221
|
+
items = @sdb.select("select * from #{@domain} where id = '123'")[:items]
|
222
|
+
attributes = items.first.values.first
|
223
|
+
attributes["__dm_metadata"].should include("v01.01.00")
|
224
|
+
attributes["__dm_metadata"].should include("table:projects")
|
225
|
+
end
|
226
|
+
end
|
162
227
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
|
2
|
+
ROOT = File.expand_path('../..', File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(File.join(ROOT,'lib'))
|
4
|
+
require 'simpledb_adapter'
|
3
5
|
require 'logger'
|
4
6
|
require 'fileutils'
|
5
7
|
require 'spec'
|
8
|
+
require 'spec/autorun'
|
6
9
|
|
7
10
|
DOMAIN_FILE_MESSAGE = <<END
|
8
11
|
!!! ATTENTION !!!
|
@@ -20,7 +23,7 @@ END
|
|
20
23
|
Spec::Runner.configure do |config|
|
21
24
|
access_key = ENV['AMAZON_ACCESS_KEY_ID']
|
22
25
|
secret_key = ENV['AMAZON_SECRET_ACCESS_KEY']
|
23
|
-
domain_file = File.
|
26
|
+
domain_file = File.join(ROOT, 'THROW_AWAY_SDB_DOMAIN')
|
24
27
|
test_domain = if File.exist?(domain_file)
|
25
28
|
File.read(domain_file).strip
|
26
29
|
else
|
@@ -30,7 +33,7 @@ Spec::Runner.configure do |config|
|
|
30
33
|
|
31
34
|
#For those that don't like to mess up their ENV
|
32
35
|
if access_key==nil && secret_key==nil
|
33
|
-
lines = File.readlines(File.join(
|
36
|
+
lines = File.readlines(File.join(ROOT, 'aws_config'))
|
34
37
|
access_key = lines[0].strip
|
35
38
|
secret_key = lines[1].strip
|
36
39
|
end
|
@@ -41,6 +44,8 @@ Spec::Runner.configure do |config|
|
|
41
44
|
log_file = "log/dm-sdb.log"
|
42
45
|
FileUtils.touch(log_file)
|
43
46
|
log = Logger.new(log_file)
|
47
|
+
log.level = ::Logger::DEBUG
|
48
|
+
DataMapper.logger.level = :debug
|
44
49
|
|
45
50
|
$control_sdb ||= RightAws::SdbInterface.new(
|
46
51
|
access_key, secret_key, :domain => test_domain)
|
@@ -0,0 +1,346 @@
|
|
1
|
+
require File.expand_path('unit_spec_helper', File.dirname(__FILE__))
|
2
|
+
require 'dm-adapter-simpledb/record'
|
3
|
+
require 'dm-adapter-simpledb/sdb_array'
|
4
|
+
|
5
|
+
describe DmAdapterSimpledb::Record do
|
6
|
+
|
7
|
+
|
8
|
+
context "given a record from SimpleDB" do
|
9
|
+
before :each do
|
10
|
+
@thing_class = Class.new do
|
11
|
+
include DataMapper::Resource
|
12
|
+
|
13
|
+
property :foo, Integer
|
14
|
+
end
|
15
|
+
@it = DmAdapterSimpledb::Record.from_simpledb_hash(
|
16
|
+
{"KEY" => {
|
17
|
+
"foo" => ["123"],
|
18
|
+
"baz" => ["456"],
|
19
|
+
"simpledb_type" => ["thingies"]
|
20
|
+
}
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return nil when asked for a non-existant attribute as String" do
|
25
|
+
@it["bar", String].should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return nil when asked for a non-existant attribute as Integer" do
|
29
|
+
@it["bar", Integer].should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return [] when asked for a non-existant attribute as Array" do
|
33
|
+
@it["bar", Array].should == []
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be able to coerce based on a property" do
|
37
|
+
@it["foo", @thing_class.properties[:foo]].should == 123
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be able to coerce based on a simple type" do
|
41
|
+
@it["foo", Integer].should == "123"
|
42
|
+
end
|
43
|
+
|
44
|
+
context "converted to a resource hash" do
|
45
|
+
before :each do
|
46
|
+
@hash = @it.to_resource_hash(@thing_class.properties)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should only include properties specified in the field set" do
|
50
|
+
@hash.should_not include(:bar)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
context "given a record with no version info" do
|
58
|
+
before :each do
|
59
|
+
@resource_class = Class.new do
|
60
|
+
include DataMapper::Resource
|
61
|
+
|
62
|
+
property :foo, Integer, :key => true
|
63
|
+
end
|
64
|
+
|
65
|
+
@it = DmAdapterSimpledb::Record.from_simpledb_hash(
|
66
|
+
{"KEY" => {
|
67
|
+
"foo" => ["123"],
|
68
|
+
"text" => [
|
69
|
+
"0001:line 1[[[NEWLINE]]]line 2",
|
70
|
+
"0002:[[[NEWLINE]]]line 3[[[NEW",
|
71
|
+
"0003:LINE]]]line 4"
|
72
|
+
],
|
73
|
+
"short_text" => ["foo[[[NEWLINE]]]bar"],
|
74
|
+
"simpledb_type" => ["thingies"],
|
75
|
+
"null_field" => ["nil"],
|
76
|
+
"array" => ["foo[[[NEWLINE]]]bar", "baz"]
|
77
|
+
}
|
78
|
+
})
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should identify the record as version 0" do
|
82
|
+
@it.version.should == "00.00.00"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should be able to convert the record to a DM-friendly hash" do
|
86
|
+
@it.to_resource_hash(@resource_class.properties).should == {
|
87
|
+
"foo" => 123,
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should be able to extract the storage name" do
|
92
|
+
@it.storage_name.should == "thingies"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should subtitute newlines for newline placeholders" do
|
96
|
+
@it["text", String].should ==
|
97
|
+
"line 1\nline 2\nline 3\nline 4"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should identify the table from the simpledb_type attributes" do
|
101
|
+
@it.table.should == "thingies"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should not write any V1.1 metadata" do
|
105
|
+
@it.writable_attributes.should_not include("__dm_metadata")
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should interpret ['nil'] as the null value" do
|
109
|
+
@it["null_field", String].should == nil
|
110
|
+
@it["null_field", Array].should == []
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "migrated to latest version" do
|
114
|
+
before :each do
|
115
|
+
@it = @it.migrate
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should be the latest version" do
|
119
|
+
@it.version.should == "01.01.00"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should mark nil attributes as deletable" do
|
123
|
+
@it.writable_attributes.should_not include("null_field")
|
124
|
+
@it.deletable_attributes.should include("null_field")
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should contain valued attributes" do
|
128
|
+
@it.writable_attributes["foo"].should == ["123"]
|
129
|
+
@it.writable_attributes["text"].should ==
|
130
|
+
["line 1\nline 2\nline 3\nline 4"]
|
131
|
+
@it.writable_attributes["short_text"].should ==
|
132
|
+
["foo\nbar"]
|
133
|
+
@it.writable_attributes["array"].should ==
|
134
|
+
["foo\nbar", "baz"]
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
it "should have writable metadata attributes" do
|
139
|
+
@it.writable_attributes["__dm_metadata"].should include("v01.01.00")
|
140
|
+
@it.writable_attributes["__dm_metadata"].should include("table:thingies")
|
141
|
+
@it.writable_attributes["simpledb_type"].should == ["thingies"]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "given a version 1.1 record" do
|
147
|
+
before :each do
|
148
|
+
@resource_class = Class.new do
|
149
|
+
include DataMapper::Resource
|
150
|
+
|
151
|
+
property :bar, Integer
|
152
|
+
end
|
153
|
+
|
154
|
+
@it = DmAdapterSimpledb::Record.from_simpledb_hash(
|
155
|
+
{"KEY" => {
|
156
|
+
"__dm_metadata" => ["v01.01.00", "table:mystuff"],
|
157
|
+
"bar" => ["456"],
|
158
|
+
"text" => [
|
159
|
+
"0001:line 1[[[NEWLINE]]]line 2",
|
160
|
+
"0002:line 3[[[NEW",
|
161
|
+
"0003:LINE]]]line 4"
|
162
|
+
],
|
163
|
+
}
|
164
|
+
})
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should be a V1 record" do
|
168
|
+
@it.should be_a_kind_of(DmAdapterSimpledb::RecordV1_1)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should identify the record as version 1" do
|
172
|
+
@it.version.should == "01.01.00"
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should be able to convert the record to a DM-friendly hash" do
|
176
|
+
@it.to_resource_hash(@resource_class.properties).should == {
|
177
|
+
"bar" => 456
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should not substitute newline tokens" do
|
182
|
+
@it["text", String].should ==
|
183
|
+
"line 1[[[NEWLINE]]]line 2line 3[[[NEWLINE]]]line 4"
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should find the table given in the metadata" do
|
187
|
+
@it.table.should == "mystuff"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "given a V1.1 record with a chunked string" do
|
192
|
+
class Poem
|
193
|
+
include ::DataMapper::Resource
|
194
|
+
property :text, String
|
195
|
+
end
|
196
|
+
|
197
|
+
before :each do
|
198
|
+
@it = DmAdapterSimpledb::Record.from_simpledb_hash(
|
199
|
+
{"KEY" => {
|
200
|
+
"__dm_metadata" => ["v01.01.00"],
|
201
|
+
"text" => [
|
202
|
+
"0002:did gyre and gimbal in the wabe",
|
203
|
+
"0001:twas brillig and the slithy toves\n",
|
204
|
+
]
|
205
|
+
}
|
206
|
+
})
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should unchunk the text when asked to read it as a String" do
|
210
|
+
@it["text",String].should == "twas brillig and the slithy toves\n" +
|
211
|
+
"did gyre and gimbal in the wabe"
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should return the chunks when asked to read it as an Array" do
|
215
|
+
@it["text",Array].should == [
|
216
|
+
"0002:did gyre and gimbal in the wabe",
|
217
|
+
"0001:twas brillig and the slithy toves\n",
|
218
|
+
]
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should return the first chunk when asked to read it as anything else" do
|
222
|
+
@it["text", Integer].should == "0002:did gyre and gimbal in the wabe"
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should be able to construct a resource hash" do
|
226
|
+
@it.to_resource_hash(Poem.properties).should == {
|
227
|
+
"text" => "twas brillig and the slithy toves\ndid gyre and gimbal in the wabe"
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "given an unsaved (new) datamapper resource" do
|
234
|
+
before :each do
|
235
|
+
@resource_class = Class.new do
|
236
|
+
include DataMapper::Resource
|
237
|
+
storage_names[:default] = "books"
|
238
|
+
|
239
|
+
property :author, String, :key => true
|
240
|
+
property :date, Date
|
241
|
+
property :text, DataMapper::Types::Text
|
242
|
+
property :tags, DataMapper::Types::SdbArray
|
243
|
+
property :isbn, String
|
244
|
+
end
|
245
|
+
@text = "lorem ipsum\n" * 100
|
246
|
+
@date = Date.new(2001,1,1)
|
247
|
+
@author = "Cicero"
|
248
|
+
@resource = @resource_class.new(
|
249
|
+
:text => @text,
|
250
|
+
:date => @date,
|
251
|
+
:author => @author,
|
252
|
+
:tags => ['latin', 'classic'],
|
253
|
+
:isbn => nil)
|
254
|
+
|
255
|
+
@it = DmAdapterSimpledb::Record.from_resource(@resource)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should be able to generate an item name" do
|
259
|
+
@it.item_name.should ==
|
260
|
+
Digest::SHA1.hexdigest("books+Cicero")
|
261
|
+
end
|
262
|
+
|
263
|
+
context "as a SimpleDB hash" do
|
264
|
+
before :each do
|
265
|
+
@hash = @it.writable_attributes
|
266
|
+
@deletes = @it.deletable_attributes
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should translate primitives successfully" do
|
270
|
+
@hash["author"].should == ["Cicero"]
|
271
|
+
@hash["date"].should == ["2001-01-01"]
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should chunk large text sections" do
|
275
|
+
@hash["text"].should have(2).chunks
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should be able to round-trip the text it chunks" do
|
279
|
+
DmAdapterSimpledb::Record.from_simpledb_hash({"NAME" => @hash})["text", String].should ==
|
280
|
+
@text
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should translate arrays properly" do
|
284
|
+
@hash["tags"].should == ['latin', 'classic']
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should be able to round-trip arrays" do
|
288
|
+
DmAdapterSimpledb::Record.from_simpledb_hash({"NAME" => @hash})["tags", DataMapper::Types::SdbArray].should ==
|
289
|
+
['latin', 'classic']
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should not include nil values in writable attributes" do
|
293
|
+
@hash.should_not include("isbn")
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should include resource type in writable attributes" do
|
297
|
+
@hash["simpledb_type"].should == ["books"]
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should include nil values in deleteable attributes" do
|
301
|
+
@deletes.should include("isbn")
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should include version in writable attributes" do
|
305
|
+
@hash["__dm_metadata"].should include("v01.01.00")
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should include type in writable attributes" do
|
309
|
+
@hash["__dm_metadata"].should include("table:books")
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe "given a saved datamapper resource" do
|
316
|
+
before :each do
|
317
|
+
@resource_class = Class.new do
|
318
|
+
include DataMapper::Resource
|
319
|
+
storage_names[:default] = "books"
|
320
|
+
|
321
|
+
property :author, String, :key => true
|
322
|
+
property :date, Date
|
323
|
+
property :text, DataMapper::Types::Text
|
324
|
+
property :tags, DataMapper::Types::SdbArray
|
325
|
+
property :isbn, String
|
326
|
+
end
|
327
|
+
@date = Date.new(2001,1,1)
|
328
|
+
@author = "Cicero"
|
329
|
+
@resource = @resource_class.new(
|
330
|
+
:text => "",
|
331
|
+
:date => @date,
|
332
|
+
:author => @author,
|
333
|
+
:tags => ['latin', 'classic'],
|
334
|
+
:isbn => nil)
|
335
|
+
@resource.stub!(:saved? => true)
|
336
|
+
@resource.stub!(:new? => false)
|
337
|
+
|
338
|
+
@it = DmAdapterSimpledb::Record.from_resource(@resource)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should not include metadata in writable attributes" do
|
342
|
+
@it.writable_attributes.should_not include("__dm_metadata")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|