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.
- data/.gitignore +6 -0
- data/.rvmrc +1 -0
- data/AUTHORS.rdoc +5 -0
- data/CHANGELOG.rdoc +64 -0
- data/INDEX.rdoc +17 -0
- data/generators/annotations_migration/annotations_migration_generator.rb +32 -0
- data/generators/annotations_migration/templates/migration_v1.rb +60 -0
- data/generators/annotations_migration/templates/migration_v2.rb +9 -0
- data/generators/annotations_migration/templates/migration_v3.rb +74 -0
- data/generators/annotations_migration/templates/migration_v4.rb +13 -0
- data/install.rb +1 -0
- data/lib/annotations/acts_as_annotatable.rb +271 -0
- data/lib/annotations/acts_as_annotation_source.rb +117 -0
- data/lib/annotations/acts_as_annotation_value.rb +115 -0
- data/lib/annotations/config.rb +148 -0
- data/lib/annotations/routing.rb +8 -0
- data/lib/annotations/util.rb +82 -0
- data/lib/annotations_version_fu.rb +119 -0
- data/lib/app/controllers/annotations_controller.rb +162 -0
- data/lib/app/controllers/application_controller.rb +2 -0
- data/lib/app/helpers/application_helper.rb +2 -0
- data/lib/app/models/annotation.rb +413 -0
- data/lib/app/models/annotation_attribute.rb +37 -0
- data/lib/app/models/annotation_value_seed.rb +48 -0
- data/lib/app/models/number_value.rb +23 -0
- data/lib/app/models/text_value.rb +23 -0
- data/my_annotations.gemspec +4 -9
- data/rails/init.rb +8 -0
- data/test/acts_as_annotatable_test.rb +186 -0
- data/test/acts_as_annotation_source_test.rb +84 -0
- data/test/acts_as_annotation_value_test.rb +17 -0
- data/test/annotation_attribute_test.rb +22 -0
- data/test/annotation_test.rb +213 -0
- data/test/annotation_value_seed_test.rb +14 -0
- data/test/annotation_version_test.rb +39 -0
- data/test/annotations_controller_test.rb +27 -0
- data/test/app_root/app/controllers/application_controller.rb +9 -0
- data/test/app_root/app/models/book.rb +5 -0
- data/test/app_root/app/models/chapter.rb +5 -0
- data/test/app_root/app/models/group.rb +3 -0
- data/test/app_root/app/models/tag.rb +6 -0
- data/test/app_root/app/models/user.rb +3 -0
- data/test/app_root/app/views/annotations/edit.html.erb +12 -0
- data/test/app_root/app/views/annotations/index.html.erb +1 -0
- data/test/app_root/app/views/annotations/new.html.erb +11 -0
- data/test/app_root/app/views/annotations/show.html.erb +3 -0
- data/test/app_root/config/boot.rb +115 -0
- data/test/app_root/config/environment.rb +16 -0
- data/test/app_root/config/environments/mysql.rb +0 -0
- data/test/app_root/config/routes.rb +4 -0
- data/test/app_root/db/migrate/001_create_test_models.rb +38 -0
- data/test/app_root/db/migrate/002_annotations_migration_v1.rb +60 -0
- data/test/app_root/db/migrate/003_annotations_migration_v2.rb +9 -0
- data/test/app_root/db/migrate/004_annotations_migration_v3.rb +72 -0
- data/test/config_test.rb +383 -0
- data/test/fixtures/annotation_attributes.yml +49 -0
- data/test/fixtures/annotation_value_seeds.csv +16 -0
- data/test/fixtures/annotation_versions.yml +259 -0
- data/test/fixtures/annotations.yml +239 -0
- data/test/fixtures/books.yml +13 -0
- data/test/fixtures/chapters.yml +27 -0
- data/test/fixtures/groups.yml +7 -0
- data/test/fixtures/number_value_versions.csv +2 -0
- data/test/fixtures/number_values.csv +2 -0
- data/test/fixtures/text_value_versions.csv +35 -0
- data/test/fixtures/text_values.csv +35 -0
- data/test/fixtures/users.yml +8 -0
- data/test/number_value_version_test.rb +40 -0
- data/test/routing_test.rb +27 -0
- data/test/test_helper.rb +41 -0
- data/test/text_value_version_test.rb +40 -0
- metadata +77 -7
@@ -0,0 +1,413 @@
|
|
1
|
+
class Annotation < ActiveRecord::Base
|
2
|
+
include AnnotationsVersionFu
|
3
|
+
|
4
|
+
belongs_to :annotatable,
|
5
|
+
:polymorphic => true
|
6
|
+
|
7
|
+
belongs_to :source,
|
8
|
+
:polymorphic => true
|
9
|
+
|
10
|
+
belongs_to :value,
|
11
|
+
:polymorphic => true,
|
12
|
+
:autosave => true
|
13
|
+
|
14
|
+
belongs_to :attribute,
|
15
|
+
:class_name => "AnnotationAttribute",
|
16
|
+
:foreign_key => "attribute_id"
|
17
|
+
|
18
|
+
belongs_to :version_creator,
|
19
|
+
:class_name => Annotations::Config.user_model_name
|
20
|
+
|
21
|
+
before_validation :process_value_generation
|
22
|
+
|
23
|
+
validates_presence_of :source_type,
|
24
|
+
:source_id,
|
25
|
+
:annotatable_type,
|
26
|
+
:annotatable_id,
|
27
|
+
:attribute_id,
|
28
|
+
:value_type
|
29
|
+
|
30
|
+
validate :check_annotatable,
|
31
|
+
:check_source,
|
32
|
+
:check_value,
|
33
|
+
:check_duplicate,
|
34
|
+
:check_limit_per_source,
|
35
|
+
:check_content_restrictions
|
36
|
+
|
37
|
+
|
38
|
+
# ========================
|
39
|
+
# Versioning configuration
|
40
|
+
# ------------------------
|
41
|
+
|
42
|
+
annotations_version_fu do
|
43
|
+
belongs_to :annotatable,
|
44
|
+
:polymorphic => true
|
45
|
+
|
46
|
+
belongs_to :source,
|
47
|
+
:polymorphic => true
|
48
|
+
|
49
|
+
belongs_to :value,
|
50
|
+
:polymorphic => true
|
51
|
+
|
52
|
+
belongs_to :attribute,
|
53
|
+
:class_name => "AnnotationAttribute",
|
54
|
+
:foreign_key => "attribute_id"
|
55
|
+
|
56
|
+
belongs_to :version_creator,
|
57
|
+
:class_name => "::#{Annotations::Config.user_model_name}"
|
58
|
+
|
59
|
+
validates_presence_of :source_type,
|
60
|
+
:source_id,
|
61
|
+
:annotatable_type,
|
62
|
+
:annotatable_id,
|
63
|
+
:attribute_id,
|
64
|
+
:value_type
|
65
|
+
|
66
|
+
# NOTE: make sure to update the logic in here
|
67
|
+
# if Annotation#value_content changes!
|
68
|
+
def value_content
|
69
|
+
self.value.nil? ? "" : self.value.ann_content
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# ========================
|
75
|
+
|
76
|
+
# Named scope to allow you to include the value records too.
|
77
|
+
# Use this to *potentially* improve performance.
|
78
|
+
named_scope :include_values, lambda {
|
79
|
+
{ :include => [ :value ] }
|
80
|
+
}
|
81
|
+
|
82
|
+
# Finder to get all annotations by a given source.
|
83
|
+
named_scope :by_source, lambda { |source_type, source_id|
|
84
|
+
{ :conditions => { :source_type => source_type,
|
85
|
+
:source_id => source_id },
|
86
|
+
:order => "created_at DESC" }
|
87
|
+
}
|
88
|
+
|
89
|
+
# Finder to get all annotations for a given annotatable.
|
90
|
+
named_scope :for_annotatable, lambda { |annotatable_type, annotatable_id|
|
91
|
+
{ :conditions => { :annotatable_type => annotatable_type,
|
92
|
+
:annotatable_id => annotatable_id },
|
93
|
+
:order => "created_at DESC" }
|
94
|
+
}
|
95
|
+
|
96
|
+
# Finder to get all annotations with a given attribute_name.
|
97
|
+
named_scope :with_attribute_name, lambda { |attrib_name|
|
98
|
+
{ :conditions => { :annotation_attributes => { :name => attrib_name } },
|
99
|
+
:joins => :attribute,
|
100
|
+
:order => "created_at DESC" }
|
101
|
+
}
|
102
|
+
|
103
|
+
# Finder to get all annotations with one of the given attribute_names.
|
104
|
+
named_scope :with_attribute_names, lambda { |attrib_names|
|
105
|
+
conditions = [attrib_names.collect{"annotation_attributes.name = ?"}.join(" or ")] | attrib_names
|
106
|
+
{ :conditions => conditions,
|
107
|
+
:joins => :attribute,
|
108
|
+
:order => "created_at DESC" }
|
109
|
+
}
|
110
|
+
|
111
|
+
# Finder to get all annotations for a given value_type.
|
112
|
+
named_scope :with_value_type, lambda { |value_type|
|
113
|
+
{ :conditions => { :value_type => value_type },
|
114
|
+
:order => "created_at DESC" }
|
115
|
+
}
|
116
|
+
|
117
|
+
# Helper class method to look up an annotatable object
|
118
|
+
# given the annotatable class name and ID.
|
119
|
+
def self.find_annotatable(annotatable_type, annotatable_id)
|
120
|
+
return nil if annotatable_type.nil? or annotatable_id.nil?
|
121
|
+
begin
|
122
|
+
return annotatable_type.constantize.find(annotatable_id)
|
123
|
+
rescue
|
124
|
+
return nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Helper class method to look up a source object
|
129
|
+
# given the source class name and ID.
|
130
|
+
def self.find_source(source_type, source_id)
|
131
|
+
return nil if source_type.nil? or source_id.nil?
|
132
|
+
begin
|
133
|
+
return source_type.constantize.find(source_id)
|
134
|
+
rescue
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def attribute_name
|
140
|
+
self.attribute.name
|
141
|
+
end
|
142
|
+
|
143
|
+
def attribute_name=(attr_name)
|
144
|
+
attr_name = attr_name.to_s.strip
|
145
|
+
self.attribute = AnnotationAttribute.find_or_create_by_name(attr_name)
|
146
|
+
end
|
147
|
+
|
148
|
+
alias_method :original_set_value=, :value=
|
149
|
+
def value=(value_in)
|
150
|
+
# Store this raw value in a temporary variable for
|
151
|
+
# later processing before the object is saved.
|
152
|
+
@raw_value = value_in
|
153
|
+
end
|
154
|
+
|
155
|
+
def value_content
|
156
|
+
self.value.nil? ? "" : self.value.ann_content
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.create_multiple(params, separator)
|
160
|
+
success = true
|
161
|
+
annotations = [ ]
|
162
|
+
errors = [ ]
|
163
|
+
|
164
|
+
annotatable = Annotation.find_annotatable(params[:annotatable_type], params[:annotatable_id])
|
165
|
+
|
166
|
+
if annotatable
|
167
|
+
values = params[:value]
|
168
|
+
|
169
|
+
# Remove value from params hash
|
170
|
+
params.delete("value")
|
171
|
+
|
172
|
+
values.split(separator).each do |val|
|
173
|
+
ann = Annotation.new(params)
|
174
|
+
ann.value = val.strip
|
175
|
+
|
176
|
+
if ann.save
|
177
|
+
annotations << ann
|
178
|
+
else
|
179
|
+
error_text = "Error(s) occurred whilst saving annotation with attribute: '#{params[:attribute_name]}', and value: #{val} - #{ann.errors.full_messages.to_sentence}."
|
180
|
+
errors << error_text
|
181
|
+
logger.info(error_text)
|
182
|
+
success = false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
else
|
186
|
+
errors << "Annotatable object doesn't exist"
|
187
|
+
success = false
|
188
|
+
end
|
189
|
+
|
190
|
+
return [ success, annotations, errors ]
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
|
195
|
+
def ok_value_object_type?
|
196
|
+
return !self.value.nil? &&
|
197
|
+
self.value.is_a?(ActiveRecord::Base) &&
|
198
|
+
self.value.class.respond_to?(:is_annotation_value)
|
199
|
+
end
|
200
|
+
|
201
|
+
def process_value_generation
|
202
|
+
if defined?(@raw_value) && !@raw_value.blank?
|
203
|
+
val = process_value_adjustments(@raw_value)
|
204
|
+
val = try_use_value_factory(val)
|
205
|
+
|
206
|
+
# Now run default value generation logic
|
207
|
+
# (as a fallback for default cases)
|
208
|
+
case val
|
209
|
+
when String, Symbol
|
210
|
+
val = TextValue.new :text => val.to_s
|
211
|
+
when Numeric
|
212
|
+
val = NumberValue.new :number => val
|
213
|
+
when ActiveRecord::Base
|
214
|
+
# Do nothing
|
215
|
+
else
|
216
|
+
self.errors.add(:value, "is not a valid value object")
|
217
|
+
end
|
218
|
+
|
219
|
+
# Set it on the ActiveRecord level now
|
220
|
+
self.original_set_value = val
|
221
|
+
|
222
|
+
# Reset the internal raw value too, in case this is rerun
|
223
|
+
@raw_value = val
|
224
|
+
end
|
225
|
+
|
226
|
+
return true
|
227
|
+
end
|
228
|
+
|
229
|
+
def process_value_adjustments(value_in)
|
230
|
+
value_out = value_in
|
231
|
+
|
232
|
+
attr_name = self.attribute_name.downcase
|
233
|
+
|
234
|
+
value_in = value_out.to_s if value_out.is_a?(Symbol)
|
235
|
+
|
236
|
+
# Make lowercase or uppercase if required
|
237
|
+
if value_out.is_a?(String)
|
238
|
+
if Annotations::Config::attribute_names_for_values_to_be_downcased.include?(attr_name)
|
239
|
+
value_out = value_out.downcase
|
240
|
+
end
|
241
|
+
if Annotations::Config::attribute_names_for_values_to_be_upcased.include?(attr_name)
|
242
|
+
value_out = value_out.upcase
|
243
|
+
end
|
244
|
+
|
245
|
+
# Apply strip text rules
|
246
|
+
Annotations::Config::strip_text_rules.each do |attr, strip_rules|
|
247
|
+
if attr_name == attr.downcase
|
248
|
+
if strip_rules.is_a? Array
|
249
|
+
strip_rules.each do |s|
|
250
|
+
value_out = value_out.gsub(s, '')
|
251
|
+
end
|
252
|
+
elsif strip_rules.is_a? String or strip_rules.is_a? Regexp
|
253
|
+
value_out = value_out.gsub(strip_rules, '')
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
return value_out
|
260
|
+
end
|
261
|
+
|
262
|
+
def try_use_value_factory(value_in)
|
263
|
+
attr_name = self.attribute_name.downcase
|
264
|
+
|
265
|
+
if Annotations::Config::value_factories.has_key?(attr_name)
|
266
|
+
return Annotations::Config::value_factories[attr_name].call(value_in)
|
267
|
+
else
|
268
|
+
return value_in
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# ===========
|
273
|
+
# Validations
|
274
|
+
# -----------
|
275
|
+
|
276
|
+
def check_annotatable
|
277
|
+
if Annotation.find_annotatable(self.annotatable_type, self.annotatable_id).nil?
|
278
|
+
self.errors.add(:annotatable_id, "doesn't exist")
|
279
|
+
return false
|
280
|
+
else
|
281
|
+
return true
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def check_source
|
286
|
+
if Annotation.find_source(self.source_type, self.source_id).nil?
|
287
|
+
self.errors.add(:source_id, "doesn't exist")
|
288
|
+
return false
|
289
|
+
else
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def check_value
|
295
|
+
ok = true
|
296
|
+
if self.value.nil?
|
297
|
+
self.errors.add(:value, "object must be provided")
|
298
|
+
ok = false
|
299
|
+
elsif !ok_value_object_type?
|
300
|
+
self.errors.add(:value, "must be a valid annotation value object")
|
301
|
+
ok = false
|
302
|
+
else
|
303
|
+
attr_name = self.attribute_name.downcase
|
304
|
+
if Annotations::Config::valid_value_types.has_key?(attr_name) &&
|
305
|
+
!([ Annotations::Config::valid_value_types[attr_name] ].flatten.include?(self.value.class.name))
|
306
|
+
self.errors.add_to_base("Annotation value is of an invalid type for attribute name: '#{attr_name}'. Provided value is a #{self.value.class.name}.")
|
307
|
+
ok = false
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
return ok
|
312
|
+
end
|
313
|
+
|
314
|
+
# This method checks whether duplicates are allowed for this particular annotation type (ie:
|
315
|
+
# for the attribute that this annotation belongs to).
|
316
|
+
# If not allowed, it checks for a duplicate existing annotation and fails if one does exist.
|
317
|
+
def check_duplicate
|
318
|
+
if ok_value_object_type?
|
319
|
+
attr_name = self.attribute_name.downcase
|
320
|
+
if Annotations::Config.attribute_names_to_allow_duplicates.include?(attr_name)
|
321
|
+
return true
|
322
|
+
else
|
323
|
+
if self.value.class.has_duplicate_annotation?(self)
|
324
|
+
self.errors.add_to_base("This annotation already exists and is not allowed to be created again.")
|
325
|
+
return false
|
326
|
+
else
|
327
|
+
return true
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# This method uses the 'limits_per_source config' setting to check whether a limit has been reached.
|
334
|
+
#
|
335
|
+
# NOTE: this check is only carried out on new records, not records that are being updated.
|
336
|
+
def check_limit_per_source
|
337
|
+
attr_name = self.attribute_name.downcase
|
338
|
+
if self.new_record? and Annotations::Config::limits_per_source.has_key?(attr_name)
|
339
|
+
options = Annotations::Config::limits_per_source[attr_name]
|
340
|
+
max = options[0]
|
341
|
+
can_replace = options[1]
|
342
|
+
|
343
|
+
unless (found_annotatable = Annotation.find_annotatable(self.annotatable_type, self.annotatable_id)).nil?
|
344
|
+
anns = found_annotatable.annotations_with_attribute_and_by_source(attr_name, self.source)
|
345
|
+
|
346
|
+
if anns.length >= max
|
347
|
+
self.errors.add_to_base("The limit has been reached for annotations with this attribute and by this source.")
|
348
|
+
return false
|
349
|
+
else
|
350
|
+
return true
|
351
|
+
end
|
352
|
+
else
|
353
|
+
return true
|
354
|
+
end
|
355
|
+
else
|
356
|
+
return true
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def check_content_restrictions
|
361
|
+
if ok_value_object_type?
|
362
|
+
attr_name = self.attribute_name.downcase
|
363
|
+
content_to_check = self.value_content
|
364
|
+
if Annotations::Config::content_restrictions.has_key?(attr_name)
|
365
|
+
options = Annotations::Config::content_restrictions[attr_name]
|
366
|
+
|
367
|
+
case options[:in]
|
368
|
+
when Array
|
369
|
+
if content_to_check.is_a?(String)
|
370
|
+
if options[:in].map{|s| s.downcase}.include?(content_to_check.downcase)
|
371
|
+
return true
|
372
|
+
else
|
373
|
+
self.errors.add_to_base(options[:error_message])
|
374
|
+
return false
|
375
|
+
end
|
376
|
+
else
|
377
|
+
if options[:in].include?(content_to_check)
|
378
|
+
return true
|
379
|
+
else
|
380
|
+
self.errors.add_to_base(options[:error_message])
|
381
|
+
return false
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
when Range
|
386
|
+
# Need to take into account that "a_string".to_i == 0
|
387
|
+
if content_to_check == "0"
|
388
|
+
if options[:in] === 0
|
389
|
+
return true
|
390
|
+
else
|
391
|
+
self.errors.add_to_base(options[:error_message])
|
392
|
+
return false
|
393
|
+
end
|
394
|
+
else
|
395
|
+
if options[:in] === content_to_check.to_i
|
396
|
+
return true
|
397
|
+
else
|
398
|
+
self.errors.add_to_base(options[:error_message])
|
399
|
+
return false
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
else
|
404
|
+
return true
|
405
|
+
end
|
406
|
+
else
|
407
|
+
return true
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
# ===========
|
413
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class AnnotationAttribute < ActiveRecord::Base
|
2
|
+
validates_presence_of :name,
|
3
|
+
:identifier
|
4
|
+
|
5
|
+
validates_uniqueness_of :name,
|
6
|
+
:case_sensitive => false
|
7
|
+
|
8
|
+
validates_uniqueness_of :identifier,
|
9
|
+
:case_sensitive => false
|
10
|
+
|
11
|
+
has_many :annotations,
|
12
|
+
:foreign_key => "attribute_id"
|
13
|
+
|
14
|
+
# If the identifier is not set, generate it before validation takes place.
|
15
|
+
# See Annotations::Config::default_attribute_identifier_template
|
16
|
+
# for more info.
|
17
|
+
#
|
18
|
+
# The rules are:
|
19
|
+
# - if an identifier is manually set, nothing happens.
|
20
|
+
# - if no identifier is set:
|
21
|
+
# - if name is enclosed in chevrons (eg: <http://...>) then the chevrons are taken out and the result is the new identifier.
|
22
|
+
# - if name is a URI beginning with http:// or urn: then this is used directly as the identifier.
|
23
|
+
# - in all other cases the identifier will be generated using the template specified by
|
24
|
+
# Annotations::Config::default_attribute_identifier_template, where '%s' in the template will be replaced with
|
25
|
+
# the transformation of 'name' through the Proc specified by Annotations::Config::attribute_name_transform_for_identifier.
|
26
|
+
def before_validation
|
27
|
+
unless self.name.blank? or !self.identifier.blank?
|
28
|
+
if self.name.match(/^<.+>$/)
|
29
|
+
self.identifier = self.name[1, self.name.length-1].chop
|
30
|
+
elsif self.name.match(/^http:\/\//) or self.name.match(/^urn:/)
|
31
|
+
self.identifier = self.name
|
32
|
+
else
|
33
|
+
self.identifier = (Annotations::Config::default_attribute_identifier_template % Annotations::Config::attribute_name_transform_for_identifier.call(self.name))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class AnnotationValueSeed < ActiveRecord::Base
|
2
|
+
validates_presence_of :attribute_id,
|
3
|
+
:value_type,
|
4
|
+
:value_id
|
5
|
+
|
6
|
+
belongs_to :value,
|
7
|
+
:polymorphic => true
|
8
|
+
|
9
|
+
belongs_to :attribute,
|
10
|
+
:class_name => "AnnotationAttribute",
|
11
|
+
:foreign_key => "attribute_id"
|
12
|
+
|
13
|
+
# Named scope to allow you to include the value records too.
|
14
|
+
# Use this to *potentially* improve performance.
|
15
|
+
named_scope :include_values, lambda {
|
16
|
+
{ :include => [ :value ] }
|
17
|
+
}
|
18
|
+
|
19
|
+
# Finder to get all annotation value seeds with a given attrib_name.
|
20
|
+
named_scope :with_attribute_name, lambda { |attrib_name|
|
21
|
+
{ :conditions => { :annotation_attributes => { :name => attrib_name } },
|
22
|
+
:joins => :attribute,
|
23
|
+
:order => "created_at DESC" }
|
24
|
+
}
|
25
|
+
|
26
|
+
# Finder to get all annotation value seeds with one of the given attrib_names.
|
27
|
+
named_scope :with_attribute_names, lambda { |attrib_names|
|
28
|
+
conditions = [attrib_names.collect{"annotation_attributes.name = ?"}.join(" or ")] | attrib_names
|
29
|
+
{ :conditions => conditions,
|
30
|
+
:joins => :attribute,
|
31
|
+
:order => "created_at DESC" }
|
32
|
+
}
|
33
|
+
|
34
|
+
# Finder to get all annotations for a given value_type.
|
35
|
+
named_scope :with_value_type, lambda { |value_type|
|
36
|
+
{ :conditions => { :value_type => value_type },
|
37
|
+
:order => "created_at DESC" }
|
38
|
+
}
|
39
|
+
|
40
|
+
def self.find_by_attribute_name(attr_name)
|
41
|
+
return [] if attr_name.blank?
|
42
|
+
|
43
|
+
AnnotationValueSeed.find(:all,
|
44
|
+
:joins => [ :attribute ],
|
45
|
+
:conditions => { :annotation_attributes => { :name => attr_name } },
|
46
|
+
:order => "created_at DESC")
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class NumberValue < ActiveRecord::Base
|
2
|
+
include AnnotationsVersionFu
|
3
|
+
|
4
|
+
validates_presence_of :number
|
5
|
+
|
6
|
+
acts_as_annotation_value :content_field => :number
|
7
|
+
|
8
|
+
belongs_to :version_creator,
|
9
|
+
:class_name => "::#{Annotations::Config.user_model_name}"
|
10
|
+
|
11
|
+
# ========================
|
12
|
+
# Versioning configuration
|
13
|
+
# ------------------------
|
14
|
+
|
15
|
+
annotations_version_fu do
|
16
|
+
validates_presence_of :number
|
17
|
+
|
18
|
+
belongs_to :version_creator,
|
19
|
+
:class_name => "::#{Annotations::Config.user_model_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# ========================
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class TextValue < ActiveRecord::Base
|
2
|
+
include AnnotationsVersionFu
|
3
|
+
|
4
|
+
validates_presence_of :text
|
5
|
+
|
6
|
+
acts_as_annotation_value :content_field => :text
|
7
|
+
|
8
|
+
belongs_to :version_creator,
|
9
|
+
:class_name => "::#{Annotations::Config.user_model_name}"
|
10
|
+
|
11
|
+
# ========================
|
12
|
+
# Versioning configuration
|
13
|
+
# ------------------------
|
14
|
+
|
15
|
+
annotations_version_fu do
|
16
|
+
validates_presence_of :text
|
17
|
+
|
18
|
+
belongs_to :version_creator,
|
19
|
+
:class_name => "::#{Annotations::Config.user_model_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# ========================
|
23
|
+
end
|
data/my_annotations.gemspec
CHANGED
@@ -1,19 +1,14 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
4
|
s.name = 'my_annotations'
|
3
|
-
s.version = '0.5.
|
5
|
+
s.version = '0.5.1'
|
4
6
|
s.date = '2013-05-02'
|
5
7
|
s.summary = "This gem allows arbitrary metadata and relationships to be stored and retrieved, in the form of Annotations for any model objects in your Ruby on Rails (v2.2+) application."
|
6
8
|
s.description = "This gem allows arbitrary metadata and relationships to be stored and retrieved, in the form of Annotations for any model objects in your Ruby on Rails (v2.2+) application."
|
7
9
|
s.authors = ["Jiten Bhagat","Stuart Owen","Quyen Nguyen"]
|
8
10
|
s.email = 'nttqa22001@yahoo.com'
|
9
|
-
s.files =
|
10
|
-
"RakeFile",
|
11
|
-
"VERSION.yml",
|
12
|
-
"LICENSE",
|
13
|
-
"script/console",
|
14
|
-
"README.rdoc",
|
15
|
-
"RUNNING_TESTS.rdoc",
|
16
|
-
"my_annotations.gemspec"]
|
11
|
+
s.files = `git ls-files`.split($/)
|
17
12
|
s.homepage = 'https://github.com/myGrid/annotations'
|
18
13
|
s.require_paths = ["lib"]
|
19
14
|
end
|