Class LogiVersion
In: app/models/logi_version.rb
Parent: ActiveRecord::Base

LogiVersions are directly coupled to Logis.

Each change (edit/save of the text_stack) of a Logi results in a new LogiVersion

See Logi for a detailed description of what LogiVersions do for Logis

Methods

Included Modules

CacheMethodsModule

External Aliases

__text_assignment_after_initialize__ -> text=

Attributes

body  [W] 
context  [RW] 
position_ranges  [RW] 

Public Class methods

Chops off a half word if it‘s at the end of the text.

[Source]

     # File app/models/logi_version.rb, line 171
171:   def self.chop_body(text, size)
172:     chopped = text[0...size]
173:     if text.size > size
174:       return chopped.gsub(/[\w]+$/,'')
175:     else
176:       return chopped
177:     end
178:   end

Cleans the submitted logi as it comes from the editor.

[Source]

     # File app/models/logi_version.rb, line 112
112:   def self.clean(text)
113:     text = text.dup
114:     # remove newlines
115:     text.gsub!(/(\r\n|\n)/, " ")
116: 
117:     # replace brs by p's, and protect double p's
118:     text.gsub!(/<br\s*\/><br\s*\/>/, "</p> <p>")
119:     text.gsub!(/<\/p>\s*<p>/, "__p_p__")
120: 
121:     # remove unprotected tags
122:     text.gsub!(/<br\s*\/>/, "")
123:     text.gsub!(/(<p>|<\/p>)/, "")
124: 
125:     # remove whitespace
126:     text.gsub!(/&nbsp;/, " ")
127:     text.squeeze!(' ')
128: 
129:     # remove cornered and double double p's
130:     text.gsub!(/__p_p__(\s*__p_p__)+/, "__p_p__")
131:     text.gsub!(/^__p_p__/, "")
132:     text.gsub!(/__p_p__$/, "")
133: 
134:     # remove whitespace again
135:     text.squeeze!(' ')
136:     text.strip!
137: 
138:     # put p's back
139:     text.gsub!(/__p_p__/, "</p> <p>")
140:     text.gsub!(/<\/h1>\s*/, "</h1> <p>")
141: 
142:     text.gsub!(/\s*<h2>/, "</p> <h2>") # also for h2's
143:     text.gsub!(/<\/h2>\s*/, "</h2> <p>")
144: 
145:     text.gsub!(/\s*<ul>/, "</p> <ul>") # ul's
146:     text.gsub!(/<\/ul>\s*/, "</ul> <p>")
147: 
148:     text.gsub!(/\s*<ol>/, "</p> <ol>") # and ol's
149:     text.gsub!(/<\/ol>\s*/, "</ol> <p>")
150:     text += "</p>"
151: 
152:     # remove empty paragraphs
153:     text.gsub!(/<p>\s*<\/p>/, "")
154: 
155:     return text
156:   end

Creates a new LogiVersion from a logi_version_hash.

Required elements are: :logi_version_nr => <the version_nr requested>

[Source]

     # File app/models/logi_version.rb, line 95
 95:   def self.from_h(logi, logi_version_hash)
 96:     if logi_version_hash[:logi_version_nr].nil?
 97:       raise HashError.new(logi_version_hash), t('m.logi_version.error_hash')
 98:     end
 99:     if logi_version_hash[:logi_version_nr] == "c"
100:       logi_version = logi.current_logi_version
101:     else
102:       logi_version = LogiVersion.find_by_logi_id_and_nr(logi.id, logi_version_hash[:logi_version_nr])
103:     end
104:     if logi_version.nil?
105:       raise ExistenceError.new(logi_version_hash), t('m.logi_version.error_nonexisting')
106:     end
107:     return logi_version
108:   end

Creates a new logi_version from a string

See hash_from_s for the syntax

[Source]

    # File app/models/logi_version.rb, line 65
65:   def self.from_s(logi, logi_version_string)
66:     LogiVersion.from_h(logi, LogiVersion.hash_from_s(logi_version_string))
67:   end

Parses a logi_version_hash from a logi_version_string.

The syntax is as follows:

v<logi version nr of the related logi>

An example:

v3

[Source]

    # File app/models/logi_version.rb, line 77
77:   def self.hash_from_s(logi_version_string)
78:     if logi_version_string !~ Const::LogiVersion::CHECK_LOGI_VERSION_RE
79:       raise ParsingError.new(logi_version_string), t('m.logi_version.error_specification')
80:     end
81:     logi_version_string =~ Const::LogiVersion::MATCH_LOGI_VERSION_RE
82:     match = $~
83:     nr = match[1]
84:     if nr != "c"
85:       nr = nr.to_i
86:     end
87:     return {:logi_version_nr => nr}
88:   end

Removes newlines, and squeezes & strips spaces at the end.

[Source]

     # File app/models/logi_version.rb, line 160
160:   def self.strip_html(text)
161:     text = text.dup
162:     # may be improved...
163:     text.gsub!(/\<.*?\>/,' ')
164:     text.squeeze!(' ')
165:     text.strip!
166:     return text
167:   end

Public Instance methods

Returns the logi-body.

Cached.

[Source]

     # File app/models/logi_version.rb, line 268
268:   def body
269:     if self.text
270:       match = self.text.match(/^.*?<\/h1> (.*)$/)
271:       if match
272:         return match[1]
273:       end
274:     end
275:   end

For position-ranges stuff…

[Source]

     # File app/models/logi_version.rb, line 254
254:   def raw_title
255:     return '<h1>' + self.title + '</h1> '
256:   end

Returns the first 300 characters of the body as the snippet.

Cached.

[Source]

     # File app/models/logi_version.rb, line 281
281:   def snippet
282:     if self.body
283:       return LogiVersion.chop_body(LogiVersion.strip_html(self.body), 300)
284:     end
285:   end

Applies the position-ranges of this version to the body of the Logi.

Cached.

[Source]

     # File app/models/logi_version.rb, line 196
196:   def text
197:     if !self.position_ranges.nil?
198:       return self.position_ranges.apply_to_string(self.logi.text_stack)
199:     end
200:   end

Creates this version with the given html

Only to be called on new LogiVersions.

[Source]

     # File app/models/logi_version.rb, line 208
208:   def text=(new_text)
209:     @new_text = new_text
210:   end

Returns the text of the first line as the title if it starts with h1.

Cached.

[Source]

     # File app/models/logi_version.rb, line 243
243:   def title
244:     if self.text
245:       match = self.text.match(/^<h1>(.+?)<\/h1>/)
246:       if match
247:         return match[1]
248:       end
249:     end
250:   end

Assigns the title and strips it.

[Source]

     # File app/models/logi_version.rb, line 260
260:   def title=(t)
261:     @title = ERB::Util.h(t)
262:   end

Turns a LogiVersion into a string.

[Source]

     # File app/models/logi_version.rb, line 186
186:   def to_s
187:     return '=v' + self.nr.to_s
188:   end

Protected Instance methods

Sets the position_ranges attribute and associates each PositionRange with it‘s position in the un-sorted list.

[Source]

     # File app/models/logi_version.rb, line 356
356:   def after_initialize
357:     self.reset_methods_cache # see CacheMethodsModule
358:     if self.new_record?
359:       self.instance_eval {
360:         alias text= __text_assignment_after_initialize__
361:       }
362:       if !@new_text.nil?
363:         self.text = @new_text
364:       elsif !@title.nil? and !@body.nil?
365:         self.text = '<h1>' + @title + '</h1> ' + @body
366:       end
367:     else
368:       @position_ranges = PositionRange::List.from_s(self.position_ranges_string)
369:     end
370:     return true
371:   end

Checks edit-permissions for this version.

[Source]

     # File app/models/logi_version.rb, line 375
375:   def check_edit_permissions
376:     if GlobalConfig.done?
377:       return (self.editor.admin? or self.logi.edit_rights?(self.editor))
378:     else
379:       return true
380:     end
381:     return true
382:   end

Destroys all links, external links and annotations that are not in the current version.

[Source]

     # File app/models/logi_version.rb, line 399
399:   def destroy_orphaned_inserts
400:     self.logi.inserts.each do |insert|
401:       lost = self.position_ranges.invert
402:       overlap = insert.position_ranges.substract(lost,
403:           :ignore_attributes => true)
404:       if overlap.range_size == 0
405:         insert.destroy
406:       end
407:     end
408:   end

Diffs the current logi_version and the logi‘s text_stack with the new_text given and returns a hash containing:

:matched_old = the position_ranges in the

    text_stack of the logi for the places where the
    new_text matches the text_stack.

:remaining_new = the position-ranges

    for the part of the new text that remains
    unmatched in the text_stack.

[Source]

     # File app/models/logi_version.rb, line 299
299:   def diff_new_text(new_text)
300:     current_version = self.logi.current_logi_version
301:     # first try diffing with the current version
302:     current_v_diff_hash = current_version.text.word_diff(new_text,
303:         :minimum_lcs_size => GlobalConfig.minimum_diff_size)
304:     matched_old = current_v_diff_hash[:matched_old].translate_from_view(
305:         current_version.position_ranges)
306:     remaining_new = current_v_diff_hash[:matched_new].invert!(new_text.size)
307:     if remaining_new.range_size > GlobalConfig.minimum_diff_size
308:       # if there remains added text, diff with the logi's text_stack
309:       body_t_diff_hash = self.diff_text_stack_with(matched_old, new_text, remaining_new)
310: 
311:       if !body_t_diff_hash[:matched_old].empty?
312:         matched_old.insert_at_ranges!(body_t_diff_hash[:matched_old],
313:             remaining_new - body_t_diff_hash[:remaining_new],
314:             body_t_diff_hash[:remaining_new])
315:         remaining_new = body_t_diff_hash[:remaining_new]
316:       end
317:     end
318:     return {:matched_old => matched_old,
319:         :remaining_new => remaining_new}
320:   end

Diffs the text_stack with the part of the given text within the remaining_new and returns a hash containing:

:matched_old = the position_ranges in the

    text_stack of the logi for the places where the
    new_text matches the text_stack.

:remaining_new = the position-ranges for the

    part of the new text that remains unmatched in the
    text_stack.

[Source]

     # File app/models/logi_version.rb, line 333
333:   def diff_text_stack_with(matched_old, new_text, remaining_new)
334:     reduced_new_text = remaining_new.apply_to_string(
335:         new_text)
336:     remaining_old = matched_old.invert(self.logi.text_stack.size)
337:     reduced_text_stack = remaining_old.apply_to_string(
338:         self.logi.text_stack)
339:     diff_hash = reduced_text_stack.word_diff(reduced_new_text,
340:         :minimum_lcs_size => GlobalConfig.minimum_diff_size)
341:     remaining_new = remaining_new - diff_hash[:matched_new].translate_from_view(
342:         remaining_new)
343:     matched_old = diff_hash[:matched_old].translate_from_view(
344:         remaining_old)
345:     return {:matched_old => matched_old,
346:         :remaining_new => remaining_new}
347:   end

Does a sanity-check on the position_ranges and turns them into a string for saving.

[Source]

     # File app/models/logi_version.rb, line 387
387:   def stringify_position_ranges
388:     if !@position_ranges.below?(self.logi.text_stack.size) # sanity check
389:       raise StandardError, t('defaults.error_end_position_range_bigger') + 
390:           ' ' + self.logi.text_stack.size.to_s
391:     end
392:     self.position_ranges_string = @position_ranges.to_s
393:     return true
394:   end

Validates length and tags.

[Source]

     # File app/models/logi_version.rb, line 416
416:   def validate
417:     # length
418:     if GlobalConfig.done_with_basics?
419:       if self.text.size > GlobalConfig.maximum_logi_version_size + 10 # <h1></h1> tags-size
420:         self.errors.add(:text, t('m.logi_version.error_too_long',
421:             :max => GlobalConfig.maximum_logi_version_size))
422:       end
423:     end
424:     # tags
425:     self.body.scan(Const::LogiVersion::HTML_TAGS).each do |tag|
426:       if not Const::LogiVersion::VALID_HTML_TAGS.include?(tag.to_s.downcase)
427:         self.errors.add(:text, t('m.logi_version.error_cannot_contain_tag',
428:             :tag => tag.to_s))
429:       end
430:     end
431:     # links
432:     if self.body =~ Const::LogiVersion::LINK_RE
433:       self.errors.add(:text, t('m.logi_version.error_cannot_contain_link'))
434:     end
435:   end

[Validate]