my_annotations 0.5.0 → 0.5.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.
Files changed (72) hide show
  1. data/.gitignore +6 -0
  2. data/.rvmrc +1 -0
  3. data/AUTHORS.rdoc +5 -0
  4. data/CHANGELOG.rdoc +64 -0
  5. data/INDEX.rdoc +17 -0
  6. data/generators/annotations_migration/annotations_migration_generator.rb +32 -0
  7. data/generators/annotations_migration/templates/migration_v1.rb +60 -0
  8. data/generators/annotations_migration/templates/migration_v2.rb +9 -0
  9. data/generators/annotations_migration/templates/migration_v3.rb +74 -0
  10. data/generators/annotations_migration/templates/migration_v4.rb +13 -0
  11. data/install.rb +1 -0
  12. data/lib/annotations/acts_as_annotatable.rb +271 -0
  13. data/lib/annotations/acts_as_annotation_source.rb +117 -0
  14. data/lib/annotations/acts_as_annotation_value.rb +115 -0
  15. data/lib/annotations/config.rb +148 -0
  16. data/lib/annotations/routing.rb +8 -0
  17. data/lib/annotations/util.rb +82 -0
  18. data/lib/annotations_version_fu.rb +119 -0
  19. data/lib/app/controllers/annotations_controller.rb +162 -0
  20. data/lib/app/controllers/application_controller.rb +2 -0
  21. data/lib/app/helpers/application_helper.rb +2 -0
  22. data/lib/app/models/annotation.rb +413 -0
  23. data/lib/app/models/annotation_attribute.rb +37 -0
  24. data/lib/app/models/annotation_value_seed.rb +48 -0
  25. data/lib/app/models/number_value.rb +23 -0
  26. data/lib/app/models/text_value.rb +23 -0
  27. data/my_annotations.gemspec +4 -9
  28. data/rails/init.rb +8 -0
  29. data/test/acts_as_annotatable_test.rb +186 -0
  30. data/test/acts_as_annotation_source_test.rb +84 -0
  31. data/test/acts_as_annotation_value_test.rb +17 -0
  32. data/test/annotation_attribute_test.rb +22 -0
  33. data/test/annotation_test.rb +213 -0
  34. data/test/annotation_value_seed_test.rb +14 -0
  35. data/test/annotation_version_test.rb +39 -0
  36. data/test/annotations_controller_test.rb +27 -0
  37. data/test/app_root/app/controllers/application_controller.rb +9 -0
  38. data/test/app_root/app/models/book.rb +5 -0
  39. data/test/app_root/app/models/chapter.rb +5 -0
  40. data/test/app_root/app/models/group.rb +3 -0
  41. data/test/app_root/app/models/tag.rb +6 -0
  42. data/test/app_root/app/models/user.rb +3 -0
  43. data/test/app_root/app/views/annotations/edit.html.erb +12 -0
  44. data/test/app_root/app/views/annotations/index.html.erb +1 -0
  45. data/test/app_root/app/views/annotations/new.html.erb +11 -0
  46. data/test/app_root/app/views/annotations/show.html.erb +3 -0
  47. data/test/app_root/config/boot.rb +115 -0
  48. data/test/app_root/config/environment.rb +16 -0
  49. data/test/app_root/config/environments/mysql.rb +0 -0
  50. data/test/app_root/config/routes.rb +4 -0
  51. data/test/app_root/db/migrate/001_create_test_models.rb +38 -0
  52. data/test/app_root/db/migrate/002_annotations_migration_v1.rb +60 -0
  53. data/test/app_root/db/migrate/003_annotations_migration_v2.rb +9 -0
  54. data/test/app_root/db/migrate/004_annotations_migration_v3.rb +72 -0
  55. data/test/config_test.rb +383 -0
  56. data/test/fixtures/annotation_attributes.yml +49 -0
  57. data/test/fixtures/annotation_value_seeds.csv +16 -0
  58. data/test/fixtures/annotation_versions.yml +259 -0
  59. data/test/fixtures/annotations.yml +239 -0
  60. data/test/fixtures/books.yml +13 -0
  61. data/test/fixtures/chapters.yml +27 -0
  62. data/test/fixtures/groups.yml +7 -0
  63. data/test/fixtures/number_value_versions.csv +2 -0
  64. data/test/fixtures/number_values.csv +2 -0
  65. data/test/fixtures/text_value_versions.csv +35 -0
  66. data/test/fixtures/text_values.csv +35 -0
  67. data/test/fixtures/users.yml +8 -0
  68. data/test/number_value_version_test.rb +40 -0
  69. data/test/routing_test.rb +27 -0
  70. data/test/test_helper.rb +41 -0
  71. data/test/text_value_version_test.rb +40 -0
  72. metadata +77 -7
@@ -0,0 +1,383 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class ConfigTest < ActiveSupport::TestCase
4
+ def setup
5
+ Annotations::Config::reset
6
+
7
+ Annotations::Config::attribute_names_for_values_to_be_downcased.concat([ "downcased_thing" ])
8
+
9
+ Annotations::Config::attribute_names_for_values_to_be_upcased.concat([ "upcased_thing" ])
10
+
11
+ Annotations::Config::strip_text_rules.update({ "tag" => [ '"', ',' ], "comma_stripped" => ',', "regex_strip" => /\d/ })
12
+
13
+ Annotations::Config::limits_per_source.update({ "rating" => 1 })
14
+
15
+ Annotations::Config::attribute_names_to_allow_duplicates.concat([ "allow_duplicates_for_this" ])
16
+
17
+ Annotations::Config::content_restrictions.update({ "rating" => { :in => 1..5, :error_message => "Please provide a rating between 1 and 5" },
18
+ "category" => { :in => [ "fruit", "nut", "fibre" ], :error_message => "Please select a valid category" } })
19
+
20
+ Annotations::Config::default_attribute_identifier_template = "http://x.com/attribute#%s"
21
+
22
+ Annotations::Config::attribute_name_transform_for_identifier = Proc.new { |name|
23
+ regex = /\.|-|:/
24
+ if name.match(regex)
25
+ name.gsub(regex, ' ').titleize.gsub(' ', '').camelize(:lower)
26
+ else
27
+ name.camelize(:lower)
28
+ end
29
+ }
30
+
31
+ Annotations::Config::value_factories["tag"] = Proc.new { |v|
32
+ case v
33
+ when String, Symbol
34
+ Tag.find_or_create_by_name(v.to_s)
35
+ else
36
+ v
37
+ end
38
+ }
39
+
40
+ Annotations::Config::valid_value_types["tag"] = "Tag"
41
+
42
+ end
43
+
44
+ def teardown
45
+ Annotations::Config.reset
46
+ end
47
+
48
+ def test_values_downcased_or_upcased
49
+ source = users(:jane)
50
+
51
+ # Should downcase
52
+
53
+ ann1 = Annotation.create(:attribute_name => "downcased_thing",
54
+ :value => "UNIque",
55
+ :source_type => source.class.name,
56
+ :source_id => source.id,
57
+ :annotatable_type => "Book",
58
+ :annotatable_id => 1)
59
+
60
+ assert ann1.valid?
61
+ assert_equal "unique", ann1.value_content
62
+
63
+ # Should upcase
64
+
65
+ ann2 = Annotation.create(:attribute_name => "upcased_thing",
66
+ :value => "UNIque",
67
+ :source_type => source.class.name,
68
+ :source_id => source.id,
69
+ :annotatable_type => "Book",
70
+ :annotatable_id => 1)
71
+
72
+ assert ann2.valid?
73
+ assert_equal "UNIQUE", ann2.value_content
74
+
75
+ # Should not do anything
76
+
77
+ ann3 = Annotation.create(:attribute_name => "dont_do_anything",
78
+ :value => "UNIque",
79
+ :source_type => source.class.name,
80
+ :source_id => source.id,
81
+ :annotatable_type => "Book",
82
+ :annotatable_id => 1)
83
+
84
+ assert ann3.valid?
85
+ assert_equal "UNIque", ann3.value_content
86
+ end
87
+
88
+ def test_strip_text_rules
89
+ source = users(:john)
90
+
91
+ # Strip 'tag'
92
+
93
+ ann1 = Annotation.create(:attribute_name => "Tag",
94
+ :value => 'v,al"ue',
95
+ :source_type => source.class.name,
96
+ :source_id => source.id,
97
+ :annotatable_type => "Book",
98
+ :annotatable_id => 1)
99
+
100
+ assert ann1.valid?
101
+ assert_equal "value", ann1.value_content
102
+
103
+ # Strip 'comma_stripped'
104
+
105
+ ann2 = Annotation.create(:attribute_name => "comma_stripped",
106
+ :value => 'v,al"ue',
107
+ :source_type => source.class.name,
108
+ :source_id => source.id,
109
+ :annotatable_type => "Book",
110
+ :annotatable_id => 1)
111
+
112
+ assert ann2.valid?
113
+ assert_equal 'val"ue', ann2.value_content
114
+
115
+ # Regexp strip
116
+
117
+ ann3 = Annotation.create(:attribute_name => "regex_strip",
118
+ :value => 'v1,al"ue23x4',
119
+ :source_type => source.class.name,
120
+ :source_id => source.id,
121
+ :annotatable_type => "Book",
122
+ :annotatable_id => 1)
123
+
124
+ assert ann3.valid?
125
+ assert_equal 'v,al"uex', ann3.value_content
126
+
127
+ # Don't strip!
128
+
129
+ ann4 = Annotation.create(:attribute_name => "dont_strip",
130
+ :value => 'v,al"ue',
131
+ :source_type => source.class.name,
132
+ :source_id => source.id,
133
+ :annotatable_type => "Book",
134
+ :annotatable_id => 1)
135
+
136
+ assert ann4.valid?
137
+ assert_equal 'v,al"ue', ann4.value_content
138
+ end
139
+
140
+ def test_limits_per_source
141
+ source = users(:john)
142
+
143
+ bk1 = Book.create
144
+
145
+ anns = bk1.annotations << Annotation.new(:attribute_name => "rating",
146
+ :value => NumberValue.new(:number => 4),
147
+ :source_type => source.class.name,
148
+ :source_id => source.id)
149
+
150
+ assert anns.length > 0
151
+ assert_equal 4, bk1.annotations(true)[0].value_content
152
+ assert_equal 1, bk1.annotations(true).length
153
+
154
+ ann2 = Annotation.new(:attribute_name => "rating",
155
+ :value => 1,
156
+ :source_type => source.class.name,
157
+ :source_id => source.id,
158
+ :annotatable_type => "Book",
159
+ :annotatable_id => bk1.id)
160
+
161
+ assert ann2.invalid?
162
+ assert !ann2.save
163
+ assert_equal 1, bk1.annotations(true).length
164
+
165
+ ann3 = bk1.annotations(true)[0]
166
+ ann3.value = 3
167
+ assert ann3.valid?
168
+ assert ann3.save
169
+ assert_equal 1, bk1.annotations(true).length
170
+
171
+ # Check that two versions of the annotation now exist
172
+ assert_equal 2, bk1.annotations[0].versions.length
173
+ end
174
+
175
+ def test_attribute_names_to_allow_duplicates
176
+ source = users(:john)
177
+
178
+ # First test the default case of not allowing duplicates...
179
+
180
+ bk1 = Book.create
181
+
182
+ ann1 = bk1.annotations << Annotation.new(:attribute_name => "no_duplicates_allowed",
183
+ :value => "Hello there",
184
+ :source_type => source.class.name,
185
+ :source_id => source.id)
186
+
187
+ assert_not_nil ann1
188
+ assert_equal 1, bk1.annotations.length
189
+
190
+ ann2 = bk1.annotations << Annotation.new(:attribute_name => "no_duplicates_allowed",
191
+ :value => "Hello there again",
192
+ :source_type => source.class.name,
193
+ :source_id => source.id)
194
+
195
+ assert_not_nil ann2
196
+ assert_equal 2, bk1.annotations(true).length
197
+
198
+ ann3 = bk1.annotations << Annotation.new(:attribute_name => "no_duplicates_allowed",
199
+ :value => "Hello there",
200
+ :source_type => source.class.name,
201
+ :source_id => source.id)
202
+
203
+ assert_equal false, ann3
204
+ assert_equal 2, bk1.annotations(true).length
205
+
206
+
207
+ # Then test the configured exceptions to the default rule...
208
+
209
+ bk2 = Book.create
210
+
211
+ ann4 = bk2.annotations << Annotation.new(:attribute_name => "allow_duplicates_for_this",
212
+ :value => "Hi there",
213
+ :source_type => source.class.name,
214
+ :source_id => source.id)
215
+
216
+ assert_not_nil ann4
217
+ assert_equal 1, bk2.annotations.length
218
+
219
+ ann5 = bk2.annotations << Annotation.new(:attribute_name => "allow_duplicates_for_this",
220
+ :value => "Hi there again",
221
+ :source_type => source.class.name,
222
+ :source_id => source.id)
223
+
224
+ assert_not_nil ann5
225
+ assert_equal 2, bk2.annotations(true).length
226
+
227
+ ann6 = bk2.annotations << Annotation.new(:attribute_name => "allow_duplicates_for_this",
228
+ :value => "Hi there",
229
+ :source_type => source.class.name,
230
+ :source_id => source.id)
231
+
232
+ assert_not_nil ann6
233
+ assert_equal 3, bk2.annotations(true).length
234
+ end
235
+
236
+ def test_content_restrictions
237
+ source1 = users(:john)
238
+ source2 = users(:jane)
239
+
240
+ # First test the default case of not restricting values...
241
+
242
+ bk1 = Book.create
243
+
244
+ ann1 = Annotation.new(:attribute_name => "allow_any_value",
245
+ :value => "Hello there",
246
+ :source_type => source1.class.name,
247
+ :source_id => source1.id,
248
+ :annotatable_type => "Book",
249
+ :annotatable_id => bk1.id)
250
+
251
+ assert ann1.valid?
252
+ assert ann1.save
253
+ assert_equal 1, bk1.annotations.length
254
+
255
+
256
+ # Then test the configured exceptions to the default rule...
257
+
258
+ bk2 = Book.create
259
+
260
+ ann2 = Annotation.new(:attribute_name => "rating",
261
+ :value => "2",
262
+ :source_type => source1.class.name,
263
+ :source_id => source1.id,
264
+ :annotatable_type => "Book",
265
+ :annotatable_id => bk2.id)
266
+
267
+ assert ann2.valid?
268
+ assert ann2.save
269
+ assert_equal 1, bk2.annotations.length
270
+
271
+
272
+ ann3 = Annotation.new(:attribute_name => "rating",
273
+ :value => "10",
274
+ :source_type => source2.class.name,
275
+ :source_id => source2.id,
276
+ :annotatable_type => "Book",
277
+ :annotatable_id => bk2.id)
278
+
279
+ assert ann3.invalid?
280
+ assert !ann3.save
281
+ assert ann3.errors.full_messages.include?("Please provide a rating between 1 and 5")
282
+ assert_equal 1, bk2.annotations(true).length
283
+
284
+
285
+ ann4 = Annotation.new(:attribute_name => "category",
286
+ :value => "fibre",
287
+ :source_type => source1.class.name,
288
+ :source_id => source1.id,
289
+ :annotatable_type => "Book",
290
+ :annotatable_id => bk2.id)
291
+
292
+ assert ann4.valid?
293
+ assert ann4.save
294
+ assert_equal 2, bk2.annotations(true).length
295
+
296
+ ann5 = Annotation.new(:attribute_name => "category",
297
+ :value => "home cooking",
298
+ :source_type => source2.class.name,
299
+ :source_id => source2.id,
300
+ :annotatable_type => "Book",
301
+ :annotatable_id => bk2.id)
302
+
303
+ assert ann5.invalid?
304
+ assert !ann5.save
305
+ assert ann5.errors.full_messages.include?("Please select a valid category")
306
+ assert_equal 2, bk2.annotations(true).length
307
+ end
308
+
309
+ def test_default_attribute_identifier_template
310
+ attrib1 = AnnotationAttribute.create(:name => "myAttribute")
311
+ assert attrib1.valid?
312
+ assert_equal "http://x.com/attribute#myAttribute", attrib1.identifier
313
+
314
+ attrib2 = AnnotationAttribute.create(:name => "http://www.example.org/annotations#details")
315
+ assert attrib2.valid?
316
+ assert_equal "http://www.example.org/annotations#details", attrib2.identifier
317
+
318
+ attrib3 = AnnotationAttribute.create(:name => "<www.example.org/annotations#details>")
319
+ assert attrib3.valid?
320
+ assert_equal "www.example.org/annotations#details", attrib3.identifier
321
+
322
+ attrib4 = AnnotationAttribute.create(:name => "hello_world-attribute:zero")
323
+ assert attrib4.valid?
324
+ assert_equal "http://x.com/attribute#helloWorldAttributeZero", attrib4.identifier
325
+ end
326
+
327
+ def test_value_factories
328
+ source = users(:john)
329
+
330
+ ann1 = Annotation.create(:attribute_name => "Tag",
331
+ :value => 'alignment',
332
+ :source_type => source.class.name,
333
+ :source_id => source.id,
334
+ :annotatable_type => "Book",
335
+ :annotatable_id => 1)
336
+
337
+ assert ann1.valid?
338
+ assert_kind_of Tag, ann1.value
339
+ assert_equal "alignment", ann1.value_content
340
+
341
+ ann2 = Annotation.new(:attribute_name => "tag",
342
+ :source_type => source.class.name,
343
+ :source_id => source.id,
344
+ :annotatable_type => "Book",
345
+ :annotatable_id => 1)
346
+
347
+ ann2.value = Tag.find_or_create_by_name("hello")
348
+
349
+ assert ann2.save
350
+ assert_equal 'hello', ann2.value_content
351
+ end
352
+
353
+ def test_valid_value_types
354
+ source = users(:john)
355
+
356
+ # Test valid one
357
+
358
+ ann1 = Annotation.new(:attribute_name => "Tag",
359
+ :value => 'smashing',
360
+ :source_type => source.class.name,
361
+ :source_id => source.id,
362
+ :annotatable_type => "Book",
363
+ :annotatable_id => 1)
364
+
365
+ assert ann1.valid?
366
+ assert ann1.save
367
+ assert_kind_of Tag, ann1.value
368
+
369
+ # Test invalid one
370
+
371
+ ann2 = Annotation.new(:attribute_name => "Tag",
372
+ :value => TextValue.new(:text => 'smashing'),
373
+ :source_type => source.class.name,
374
+ :source_id => source.id,
375
+ :annotatable_type => "Book",
376
+ :annotatable_id => 1)
377
+
378
+
379
+ assert ann2.invalid?
380
+ assert !ann2.save
381
+ assert ann2.errors.full_messages.include?("Annotation value is of an invalid type for attribute name: 'tag'. Provided value is a TextValue.")
382
+ end
383
+ end
@@ -0,0 +1,49 @@
1
+ aa_tag:
2
+ id: 1
3
+ name: Tag
4
+ identifier: "http://www.example.org/attribute#Tag"
5
+
6
+ aa_summary:
7
+ id: 2
8
+ name: Summary
9
+ identifier: "http://www.example.org/attribute#Summary"
10
+
11
+ aa_length:
12
+ id: 3
13
+ name: length
14
+ identifier: "http://www.example.org/attribute#Length"
15
+
16
+ aa_title:
17
+ id: 4
18
+ name: Title
19
+ identifier: "http://www.example.org/attribute#Title"
20
+
21
+ aa_complexity:
22
+ id: 5
23
+ name: complexity
24
+ identifier: "http://www.example.org/attribute#complexity"
25
+
26
+ aa_endingtype:
27
+ id: 6
28
+ name: endingType
29
+ identifier: "http://www.example.org/attribute#endingType"
30
+
31
+ aa_note:
32
+ id: 7
33
+ name: Note
34
+ identifier: "http://www.example.org/attribute#Note"
35
+
36
+ aa_author:
37
+ id: 8
38
+ name: Author
39
+ identifier: "http://www.example.org/attribute#Author"
40
+
41
+ aa_rating:
42
+ id: 9
43
+ name: rating
44
+ identifier: "http://www.example.org/attribute#rating"
45
+
46
+ aa_contextualtag:
47
+ id: 10
48
+ name: Contextual Tag
49
+ identifier: "http://www.example.org/attribute#ContextualTag"
@@ -0,0 +1,16 @@
1
+ id,attribute_id,value_type,value_id
2
+ 1,1,TextValue,1
3
+ 2,1,TextValue,2
4
+ 3,1,TextValue,3
5
+ 4,1,TextValue,4
6
+ 5,1,TextValue,5
7
+ 6,1,TextValue,6
8
+ 7,1,TextValue,7
9
+ 8,1,TextValue,8
10
+ 9,1,TextValue,9
11
+ 10,1,TextValue,10
12
+ 11,8,TextValue,11
13
+ 12,8,TextValue,12
14
+ 13,8,TextValue,13
15
+ 14,8,TextValue,14
16
+ 15,8,TextValue,15
@@ -0,0 +1,259 @@
1
+ bh_summary_1_av1:
2
+ annotation: bh_summary_1
3
+ annotatable_type: Book
4
+ annotatable_id: 1
5
+ attribute_id: 2
6
+ value_type: TextValue
7
+ value_id: 16
8
+ source_type: User
9
+ source_id: 1
10
+ version: 1
11
+ created_at: <%= 1.minute.ago.to_s(:db) %>
12
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
13
+
14
+ bh_length_1_av1:
15
+ annotation: bh_length_1
16
+ annotatable_type: Book
17
+ annotatable_id: 1
18
+ attribute_id: 3
19
+ value_type: NumberValue
20
+ value_id: 1
21
+ source_type: Group
22
+ source_id: 2
23
+ version: 1
24
+ created_at: <%= 1.minute.ago.to_s(:db) %>
25
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
26
+
27
+ bh_title_1_av1:
28
+ annotation: bh_title_1
29
+ annotatable_type: Book
30
+ annotatable_id: 1
31
+ attribute_id: 4
32
+ value_type: TextValue
33
+ value_id: 17
34
+ source_type: User
35
+ source_id: 1
36
+ version: 1
37
+ created_at: <%= 1.minute.ago.to_s(:db) %>
38
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
39
+
40
+ bh_rating_1_av1:
41
+ annotation: bh_rating_1
42
+ annotatable_type: Rating
43
+ annotatable_id: 1
44
+ attribute_id: 9
45
+ value_type: TextValue
46
+ value_id: 18
47
+ source_type: User
48
+ source_id: 2
49
+ version: 1
50
+ created_at: <%= 1.minute.ago.to_s(:db) %>
51
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
52
+
53
+ bh_tag_1_av1:
54
+ annotation: bh_tag_1
55
+ annotatable_type: Book
56
+ annotatable_id: 1
57
+ attribute_id: 1
58
+ value_type: TextValue
59
+ value_id: 19
60
+ source_type: Group
61
+ source_id: 2
62
+ version: 1
63
+ created_at: <%= 1.minute.ago.to_s(:db) %>
64
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
65
+
66
+ bh_tag_2_av1:
67
+ annotation: bh_tag_2
68
+ annotatable_type: Book
69
+ annotatable_id: 1
70
+ attribute_id: 1
71
+ value_type: TextValue
72
+ value_id: 20
73
+ source_type: User
74
+ source_id: 2
75
+ version: 1
76
+ created_at: <%= 1.minute.ago.to_s(:db) %>
77
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
78
+
79
+ br_summary_1_av1:
80
+ annotation: br_summary_1
81
+ annotatable_type: Book
82
+ annotatable_id: 2
83
+ attribute_id: 2
84
+ value_type: TextValue
85
+ value_id: 21
86
+ source_type: User
87
+ source_id: 1
88
+ version: 1
89
+ created_at: <%= 1.minute.ago.to_s(:db) %>
90
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
91
+
92
+ br_summary_2_av1:
93
+ annotation: br_summary_2
94
+ annotatable_type: Book
95
+ annotatable_id: 2
96
+ attribute_id: 2
97
+ value_type: TextValue
98
+ value_id: 22
99
+ source_type: Group
100
+ source_id: 1
101
+ version: 1
102
+ created_at: <%= 1.minute.ago.to_s(:db) %>
103
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
104
+
105
+ br_author_1_av1:
106
+ annotation: br_author_1
107
+ annotatable_type: Book
108
+ annotatable_id: 2
109
+ attribute_id: 8
110
+ value_type: TextValue
111
+ value_id: 23
112
+ source_type: User
113
+ source_id: 1
114
+ version: 1
115
+ created_at: <%= 1.minute.ago.to_s(:db) %>
116
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
117
+
118
+ br_tag_1_av1:
119
+ annotation: br_tag_1
120
+ annotatable_type: Book
121
+ annotatable_id: 2
122
+ attribute_id: 1
123
+ value_type: TextValue
124
+ value_id: 24
125
+ source_type: User
126
+ source_id: 2
127
+ version: 1
128
+ created_at: <%= 1.minute.ago.to_s(:db) %>
129
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
130
+
131
+ br_note_1_av1:
132
+ annotation: br_note_1
133
+ annotatable_type: Book
134
+ annotatable_id: 2
135
+ attribute_id: 7
136
+ value_type: TextValue
137
+ value_id: 25
138
+ source_type: User
139
+ source_id: 2
140
+ version: 1
141
+ created_at: <%= 1.minute.ago.to_s(:db) %>
142
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
143
+
144
+ bh_c10_endingtype_1_av1:
145
+ annotation: bh_c10_endingtype_1
146
+ annotatable_type: Chapter
147
+ annotatable_id: 2
148
+ attribute_id: 6
149
+ value_type: TextValue
150
+ value_id: 26
151
+ source_type: Group
152
+ source_id: 1
153
+ version: 1
154
+ created_at: <%= 1.minute.ago.to_s(:db) %>
155
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
156
+
157
+ bh_c10_title_1_av1:
158
+ annotation: bh_c10_title_1
159
+ annotatable_type: Chapter
160
+ annotatable_id: 2
161
+ attribute_id: 4
162
+ value_type: TextValue
163
+ value_id: 27
164
+ source_type: User
165
+ source_id: 2
166
+ version: 1
167
+ created_at: <%= 1.minute.ago.to_s(:db) %>
168
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
169
+
170
+ br_c2_title_1_av1:
171
+ annotation: br_c2_title_1
172
+ annotatable_type: Chapter
173
+ annotatable_id: 3
174
+ attribute_id: 4
175
+ value_type: TextValue
176
+ value_id: 28
177
+ source_type: User
178
+ source_id: 1
179
+ version: 1
180
+ created_at: <%= 1.minute.ago.to_s(:db) %>
181
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
182
+
183
+ br_c2_tag_1_av1:
184
+ annotation: br_c2_tag_1
185
+ annotatable_type: Chapter
186
+ annotatable_id: 3
187
+ attribute_id: 1
188
+ value_type: TextValue
189
+ value_id: 29
190
+ source_type: Group
191
+ source_id: 2
192
+ version: 1
193
+ created_at: <%= 1.minute.ago.to_s(:db) %>
194
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
195
+
196
+ br_c2_tag_2_av1:
197
+ annotation: br_c2_tag_2
198
+ annotatable_type: Chapter
199
+ annotatable_id: 3
200
+ attribute_id: 1
201
+ value_type: TextValue
202
+ value_id: 30
203
+ source_type: User
204
+ source_id: 1
205
+ version: 1
206
+ created_at: <%= 1.minute.ago.to_s(:db) %>
207
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
208
+
209
+ br_c2_title_2_av1:
210
+ annotation: br_c2_title_2
211
+ annotatable_type: Chapter
212
+ annotatable_id: 3
213
+ attribute_id: 4
214
+ value_type: TextValue
215
+ value_id: 31
216
+ source_type: User
217
+ source_id: 2
218
+ version: 1
219
+ created_at: <%= 1.minute.ago.to_s(:db) %>
220
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
221
+
222
+ br_c202_complexity_1_av1:
223
+ annotation: br_c202_complexity_1
224
+ annotatable_type: Chapter
225
+ annotatable_id: 4
226
+ attribute_id: 5
227
+ value_type: TextValue
228
+ value_id: 32
229
+ source_type: Group
230
+ source_id: 1
231
+ version: 1
232
+ created_at: <%= 1.minute.ago.to_s(:db) %>
233
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
234
+
235
+ br_c202_title_1_av1:
236
+ annotation: br_c202_title_1
237
+ annotatable_type: Chapter
238
+ annotatable_id: 4
239
+ attribute_id: 4
240
+ value_type: TextValue
241
+ value_id: 33
242
+ source_type: User
243
+ source_id: 1
244
+ version: 1
245
+ created_at: <%= 1.minute.ago.to_s(:db) %>
246
+ updated_at: <%= 1.minute.ago.to_s(:db) %>
247
+
248
+ br_c202_tag_1_av1:
249
+ annotation: br_c202_tag_1
250
+ annotatable_type: Chapter
251
+ annotatable_id: 4
252
+ attribute_id: 1
253
+ value_type: TextValue
254
+ value_id: 34
255
+ source_type: Group
256
+ source_id: 2
257
+ version: 1
258
+ created_at: <%= 1.minute.ago.to_s(:db) %>
259
+ updated_at: <%= 1.minute.ago.to_s(:db) %>