Class Logi
In: app/models/logi.rb
Parent: ActiveRecord::Base

The Philosopy of Logis

Logis are the articles within LogiLogi. Logis are short in size (preferably max 3 normal book-pages) and they should focus on one idea, and make only one point or tag clear. Trains of thought that are more complex should be dissected into separate Logis and be globally described in a single Logi that links to the others giving increased levels of detail. Of course it is also possible to first start with the more general level, and to extend it in- depth as needed.

The central reasons for this are:

  • Differentiation - allowing readers to read things for different needs and at different levels of detail, zooming in and following and adding links only as needed.
  • Specialization - prevent duplication of work, and allowing (un-coordinated) cooperation.
  • Streamlining - allow faster, easier and more targeted communication of ideas than in article-sized communications.
  • Integration - allow for easier linking between, bringing together, and bulding upon idea‘s.

The format of Logis

Logis an LogiVersions work in concert. The text_stack of a Logi is filtered by LogiVersions to only show the contents that remain in that LogiVersion. After editing a Logi, the additions are appended to the text_stack of the Logi, and the character-ranges are added to the position_ranges of the LogiVersion. Likewise if something is removed in a newer version, only the position_ranges are removed, not the text_stack.

This allows the position_ranges of Links and Authorships to remain the same between LogiVersions.

Methods

Included Modules

CacheMethodsModule TagsStringsModule ConstantNamedScopesModule

Public Class methods

Creates a Logi from a logi_hash.

A logi_hash can contain the following elements: :logi_id => <the id of the existing Logi>

[Source]

     # File app/models/logi.rb, line 170
170:   def self.from_h(logi_hash)
171:     if logi_hash[:perma_id].nil?
172:       raise HashError.new(logi_hash), t('m.logi.error_invalid_hash')
173:     end
174:     logi = Logi.find_by_perma_id(logi_hash[:perma_id])
175:     if logi.nil?
176:       raise ExistenceError.new(logi_hash), t('m.logi.error_nonexisting')
177:     end
178:     return logi
179:   end

Parses a Logi from a string

See hash_from_s for the syntax.

[Source]

     # File app/models/logi.rb, line 144
144:   def self.from_s(logi_string)
145:     return Logi.from_h(Logi.hash_from_s(logi_string))
146:   end

Creates a logi_hash from a logi_string.

The syntax is as follows:

i<logi id>

An example:

i7

[Source]

     # File app/models/logi.rb, line 156
156:   def self.hash_from_s(logi_string)
157:     if logi_string !~ Const::Logi::CHECK_LOGI_RE
158:       raise ParsingError.new(logi_string), t('m.logi.error_wrong_spec')
159:     end
160:     logi_string =~ Const::Logi::MATCH_LOGI_RE
161:     match = $~
162:     return {:perma_id => match[1]}
163:   end

Getters

[Source]

     # File app/models/logi.rb, line 132
132:   def self.main_page
133:     link = Link.new_from_s(GlobalConfig.main_page_tag.to_s)
134:     link.resolve
135:     return link.volatile_to_logi
136:   end

Shortcut for creation.

Options:

  • title = title for the new Logi
  • body = body for the new Logi
  • text = text for the new logi (conflicts with title and body)
  • creator = the creating & editing user
  • user_group = the user_group this Logi belongs to,
  • restriction = the edit-restriction defaults to the user_group‘s default_restriction

Returns the new Logi.

[Source]

     # File app/models/logi.rb, line 196
196:   def self.new_with(options = {})
197:     options[:restriction] ||= options[:user_group].default_restriction
198:     logi = Logi.new(:creator => options[:creator],
199:         :user_group => options[:user_group],
200:         :restriction => options[:restriction],
201:         :kind => options[:kind])
202:     logi.logi_versions.build(:logi => logi,
203:         :title => options[:title], :body => options[:body],
204:         :text => options[:text], :editor => options[:creator])
205:     return logi
206:   end

Changes

[Source]

     # File app/models/logi.rb, line 210
210:   def self.paginate_recent_changes(options)
211:     paginate_by_sql('SELECT DISTINCT l.* FROM logis AS l,' +
212:         ' (SELECT logi_id, MAX(created_at) AS created_at' +
213:         ' FROM logi_versions GROUP BY logi_id ) AS v' +
214:         ' WHERE l.id = v.logi_id ORDER BY v.created_at DESC', options)
215:   end

Public Instance methods

Adds the links at the positions given by the link_p_r_list.

(links themselves should be set as the link attribute of the PositionRanges in the list).

NOTE: That no checking is done for overlap and the like.

[Source]

     # File app/models/logi.rb, line 402
402:   def add_links_to_position_ranges(link_p_r_list)
403:     link_p_r_list.each do |link_p_r|
404:       if self.from_links.detect {|li| link_p_r.link.eql?(li)} # eql? uses <=>
405:         link = self.from_links.detect {|li| li.eql?(link_p_r.link)}
406:         link_p_r.link = link
407:         link.position_ranges << link_p_r
408:         link.save
409:       else
410:         link = link_p_r.link
411:         link.position_ranges << link_p_r
412:         self.from_links << link
413:       end
414:     end
415:   end

Returns true if the given user has admin-rights for this Logi.

[Source]

     # File app/models/logi.rb, line 263
263:   def admin_rights?(user)
264:     if (self.creator.anonymous? or user == self.creator or
265:         self.user_group.admin_permissions?(user))
266:       return true
267:     else
268:       return false
269:     end
270:   end

For the Search-index, returns the creator string.

[Source]

     # File app/models/logi.rb, line 607
607:   def creator_for_index
608:     self.creator.name
609:   end

For the Search-index, returns the html of the current logi_version.

[Source]

     # File app/models/logi.rb, line 633
633:   def current_body_for_index
634:     self.current_logi_version.body
635:   end

For the Search-index, returns the editor string of the current logi_version.

[Source]

     # File app/models/logi.rb, line 613
613:   def current_editor_for_index
614:     self.current_logi_version.editor.name
615:   end

Returns the latest version for this Logi, whether saved already or not.

[Source]

     # File app/models/logi.rb, line 244
244:   def current_logi_version
245:     return self.logi_versions.last
246:   end

Returns a smaller version of the current snippet.

[Source]

     # File app/models/logi.rb, line 336
336:   def current_tiny_snippet
337:     return LogiVersion.chop_body(self.current_snippet, 150)  
338:   end

For the Search-index, returns the title string of the current logi_version.

[Source]

     # File app/models/logi.rb, line 627
627:   def current_title_for_index
628:     self.current_logi_version.title
629:   end

Returns true if the given user has edit-rights for this Logi.

[Source]

     # File app/models/logi.rb, line 274
274:   def edit_rights?(user)
275:     if (self.restriction != Const::Logi::EDIT or
276:         user == self.creator or
277:         self.user_group.member?(user) or
278:         user.admin?)
279:       return true
280:     else
281:       return false
282:     end
283:   end

Returns true if the logi is of a home-page kind.

[Source]

     # File app/models/logi.rb, line 307
307:   def home_page?
308:     return (self.user_home_page? or 
309:         self.user_group_home_page? or self.peer_group_home_page?)
310:   end

Returns all things that are inserted in this logi.

[Source]

     # File app/models/logi.rb, line 342
342:   def inserts
343:     return self.annotations + self.from_links + self.external_links
344:   end

Returns the main link.

[Source]

     # File app/models/logi.rb, line 223
223:   def link
224:     link = Link.new(:requested_tags => self.tags.dup,
225:         :received_tags => self.tags.dup,
226:         :to_logi => self)
227:     link.link_matches.build(:logi => self)
228:     return link
229:   end

Returns true if the given version is different from the current logi version.

[Source]

     # File app/models/logi.rb, line 328
328:   def logi_version_different?(logi_version)
329:     current = self.current_logi_version
330:     return (current.title != logi_version.title or
331:         current.body != logi_version.body)
332:   end

Returns true if it‘s a presentable logi, which means it‘s not a sandbox-logi.

[Source]

     # File app/models/logi.rb, line 321
321:   def presentable?
322:     self.tags.size != 1 or (self.tags.first != Tag.sandbox)
323:   end

Returns the previous logi_version (can be the one currently saved in the db, as opposed to the one being constructed).

[Source]

     # File app/models/logi.rb, line 251
251:   def previous_logi_version
252:     return self.logi_versions.before_last
253:   end

Returns the rating this logi received for the provided peer_group.

[Source]

     # File app/models/logi.rb, line 233
233:   def rating_for(peer_group)
234:     rating = self.ratings.find_by_peer_group_id(peer_group)
235:     if rating.nil?
236:       rating = self.ratings.build(:peer_group => peer_group)
237:     end
238:     return rating
239:   end

Removes the links at the positions given by the link_p_r_list

(links themselves should be set as the link attribute of the PositionRanges in the list).

[Source]

     # File app/models/logi.rb, line 383
383:   def remove_links_from_position_ranges(link_p_r_list)
384:     link_p_r_list.each do |link_p_r|
385:       link = self.from_links.detect {|link| link == link_p_r.link}
386:       link.position_ranges.delete!(link_p_r)
387:       if link.position_ranges.empty?
388:         self.from_links.delete(link)
389:       else
390:         link.save
391:       end
392:     end
393:   end

Maintenance

[Source]

     # File app/models/logi.rb, line 436
436:   def roll_back_version
437:     self.current_logi_version.destroy
438:     pos = self.text_stack.rindex(DiffLCS::WordSplitArray::SEPARATOR)
439:     self.text_stack = self.text_stack[0...pos]
440:     self.save
441:   end

Sets the given link as the logi‘s link.

Don‘t use any other method for tagging logis.

[Source]

     # File app/models/logi.rb, line 368
368:   def tag_with_link(link)
369:     self.tags = link.requested_tags
370:   end

Returns true if this Logi is tagged with the given tag.

[Source]

     # File app/models/logi.rb, line 257
257:   def tagged_with?(tag)
258:     return self.tags.include?(tag)
259:   end

For the Search-Index, returns a string with all the tags

[Source]

     # File app/models/logi.rb, line 619
619:   def tags_for_index
620:     self.tags.collect {|t|
621:           t.to_s(:for => :show) + (t.to_s(:for => :show) != t.to_s ? ' ' + t.to_s : '')
622:         }.join(' ')
623:   end

For the Link-search index, returns the tags of the Logi.

[Source]

     # File app/models/logi.rb, line 644
644:   def tags_for_term_index
645:     self.tags.collect {|t| t.to_s}.join(' ').downcase
646:   end

Parses a Logi to a string.

Options

  • :for => :use (default), :show

[Source]

     # File app/models/logi.rb, line 424
424:   def to_s(options = {})
425:     if options[:for] == :show
426:       return self.creator.name
427:     elsif options[:for] == :full_show
428:       return self.creator.name + ', logi ' + self.perma_id.split('_').last
429:     else
430:       return '=' + self.perma_id
431:     end
432:   end

Removes all tags and makes this an untagged logi.

[Source]

     # File app/models/logi.rb, line 374
374:   def untag
375:     self.tags = [Tag.untagged]
376:   end

Returns true if the logi is tagless.

[Source]

     # File app/models/logi.rb, line 314
314:   def untagged?
315:     return (self.tags.size == 1 and self.tags.first == Tag.untagged)
316:   end

Saves the given new logi_version if it changed.

Saves only the logi if it‘s tags changed.

Note that validation of both the logi and the logi version has to be done before calling this method.

[Source]

     # File app/models/logi.rb, line 355
355:   def update_if_changed(logi_version)
356:     if self.logi_version_different?(logi_version)
357:       self.logi_versions << logi_version
358:       self.save
359:     elsif self.tags_strings_before_validation and self.tags_string_changed?
360:       self.save
361:     end 
362:   end

Protected Instance methods

Initializes the tags_strings.

[Source]

     # File app/models/logi.rb, line 449
449:   def after_initialize
450:     self.reset_methods_cache # see CacheMethodsModule
451:     self.tags_strings_after_initialize # See TagsStringsModule
452:     if self.kind.nil?
453:       self.kind = Const::Logi::KINDS[:normal]
454:     end
455:     return true
456:   end

Logs creation to log.logilogi.org

[Source]

     # File app/models/logi.rb, line 501
501:   def log_create_to_log_log
502:     if GlobalConfig.use_log_log and !self.home_page?
503:       m = LogLogMessage.new(
504:           :kind => "Logi",
505:           :channel_names => ['all','logis',
506:               'by_' + self.creator.unix_name].join(','),
507:           :title => self.current_title,
508:           :author => self.creator.name,
509:           :text => self.current_snippet,
510:           :url => GlobalConfig.site_url + self.link.to_s)
511:       m.save
512:     end
513:   end

Logs save & change to log.logilogi.org

[Source]

     # File app/models/logi.rb, line 517
517:   def log_save_to_log_log
518:     if GlobalConfig.use_log_log
519:       if self.text_stack_changed?
520:         if self.current_logi_version.nr == 1
521:           change = "Create"
522:         else
523:           change = "Edit"
524:         end
525:       elsif self.tags_string_changed?
526:         change = "Tagging"
527:       else
528:         change = nil
529:       end
530:       if change
531:         m = LogLogMessage.new(
532:             :kind => change,
533:             :channel_name => 'changes',
534:             :author => self.creator.name,
535:             :title => self.current_title,
536:             :text => 'Current version: ' + self.current_logi_version.nr.to_s,
537:             :url => GlobalConfig.site_url + self.link.to_s)
538:         m.save
539:       end
540:     end
541:   end

Destroys the tags.

Needed because :dependent => destroy calls destroy before destroying the Logi object…

[Source]

     # File app/models/logi.rb, line 562
562:   def release_used_tags
563:     self.tags.each {|t|
564:       UsedTag.release(t)
565:     }
566:     return true
567:   end

Sets the perma_id to the creator-user + the next perma-nr for that user…

[Source]

     # File app/models/logi.rb, line 461
461:   def set_perma_id
462:     creator = self.creator
463:     self.perma_id = self.creator.home_page_tag.to_s + '_' +
464:         creator.next_logi_nr.to_s
465:     creator.increment!(:next_logi_nr)
466:     return true
467:   end

Sets re_resolve to true on links that matched this logi.

[Source]

     # File app/models/logi.rb, line 545
545:   def set_re_resolve_on_matchless_links
546:     self.link_matches.each do |l_m|
547:       link = l_m.link
548:       # zero already (just) before_destroy
549:       if link.link_matches.size == 0 or !link.to_logi.nil?
550:         link.re_resolve = true
551:         link.save
552:       end
553:     end
554:     return true
555:   end

Sets re_resolve to true on to_logi links.

[Source]

     # File app/models/logi.rb, line 490
490:   def set_re_resolve_on_to_links
491:     if self.tags_string_changed?
492:       self.to_links.each do |link|
493:         link.re_resolve = true
494:         link.save
495:       end
496:     end
497:   end

Stores the tags as used tags.

[Source]

     # File app/models/logi.rb, line 471
471:   def use_or_release_tags
472:     if self.tags_string_changed?
473:       if !self.tags_string_was.nil?
474:         old_tags = self.tags_string_was.split('/').collect {|s|
475:             Tag.from_s(s)}
476: 
477:         old_tags.each do |t|
478:           UsedTag.release(t)
479:         end
480:       end
481:       self.tags.each do |t|
482:         UsedTag.use(t)
483:       end
484:     end
485:     return true
486:   end

Validates presence of tags and non-overlap with user page.

[Source]

     # File app/models/logi.rb, line 575
575:   def validate
576:     # size
577:     if GlobalConfig.done_with_basics?
578:       if self.text_stack.size > GlobalConfig.maximum_logi_size
579:         self.errors.add(:changes_history, t('m.logi.error_too_long',
580:             :max => GlobalConfig.maximum_logi_size))
581:       end
582:     end
583:     # tags
584:     if self.tags.include?(Tag.replace)
585:       self.errors.add(:link, t('m.logi.error_link_should_not_include') + ' ' +
586:           Tag.replace.to_s)
587:     end
588:     if self.tags.empty?
589:       self.errors.add(:link, t('m.logi.error_is_empty')) 
590:     end
591:     if self.tags_string_changed?
592:       if self.home_page? and !self.new_record?
593:         self.errors.add(:tags, t('m.logi.error_cannot_change'))
594:       end
595:       if !self.home_page? and self.link.to_home_page?
596:         self.errors.add(:tags, t('m.logi.error_cannot_contend'))
597:       end
598:     end
599:   end

[Validate]