Class: Thoth::Post
- Inherits:
-
Sequel::Model
- Object
- Sequel::Model
- Thoth::Post
- Includes:
- Helper::Wiki
- Defined in:
- lib/thoth/model/post.rb
Class Method Summary collapse
-
.get(name) ⇒ Object
Gets the published post with the specified name, where name can be either a name or an id.
-
.name_unique?(name) ⇒ Boolean
Returns true if the specified post name is already taken or is a reserved name.
-
.name_valid?(name) ⇒ Boolean
Returns true if the specified post name consists of valid characters and is not too long or too short.
-
.recent(page = 1, limit = 10) ⇒ Object
Gets a paginated dataset of recent published posts sorted in reverse order by creation time.
-
.recent_drafts(page = 1, limit = 10) ⇒ Object
Gets a paginated dataset of recent draft posts sorted in reverse order by creation time.
-
.suggest_name(title) ⇒ Object
Returns a valid, unique post name based on the specified title.
Instance Method Summary collapse
-
#atom_url ⇒ Object
Gets the Atom feed URL for this post.
- #body=(body) ⇒ Object
-
#comments ⇒ Object
Gets a dataset of visible comments attached to this post, ordered by creation time.
-
#created_at(format = nil) ⇒ Object
Gets the creation time of this post.
- #name=(name) ⇒ Object
-
#tags ⇒ Object
Gets an Array of tags attached to this post, ordered by name.
- #tags=(tag_names) ⇒ Object
- #title=(title) ⇒ Object
-
#updated_at(format = nil) ⇒ Object
Gets the time this post was last updated.
-
#url ⇒ Object
Gets the URL for this post.
- #validate ⇒ Object
Class Method Details
.get(name) ⇒ Object
Gets the published post with the specified name, where name can be either a name or an id. Does not return drafts.
59 60 61 62 63 64 65 |
# File 'lib/thoth/model/post.rb', line 59 def self.get(name) return Post[:id => name, :is_draft => false] if name.is_a?(Numeric) name = name.to_s.downcase name =~ /^\d+$/ ? Post[:id => name, :is_draft => false] : Post[:name => name, :is_draft => false] end |
.name_unique?(name) ⇒ Boolean
Returns true if the specified post name is already taken or is a reserved name.
69 70 71 72 73 |
# File 'lib/thoth/model/post.rb', line 69 def self.name_unique?(name) !PostController.methods.include?(name) && !PostController.instance_methods.include?(name) && !Post[:name => name.to_s.downcase] end |
.name_valid?(name) ⇒ Boolean
Returns true if the specified post name consists of valid characters and is not too long or too short.
77 78 79 |
# File 'lib/thoth/model/post.rb', line 77 def self.name_valid?(name) !!(name =~ /^[0-9a-z_-]{1,64}$/i) && !(name =~ /^[0-9]+$/) end |
.recent(page = 1, limit = 10) ⇒ Object
Gets a paginated dataset of recent published posts sorted in reverse order by creation time. Does not return drafts.
83 84 85 86 |
# File 'lib/thoth/model/post.rb', line 83 def self.recent(page = 1, limit = 10) filter(:is_draft => false).reverse_order(:created_at).paginate(page, limit) end |
.recent_drafts(page = 1, limit = 10) ⇒ Object
Gets a paginated dataset of recent draft posts sorted in reverse order by creation time. Does not return published posts.
90 91 92 93 |
# File 'lib/thoth/model/post.rb', line 90 def self.recent_drafts(page = 1, limit = 10) filter(:is_draft => true).reverse_order(:created_at).paginate(page, limit) end |
.suggest_name(title) ⇒ Object
Returns a valid, unique post name based on the specified title. If the title is empty or cannot be converted into a valid name, an empty string will be returned.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/thoth/model/post.rb', line 98 def self.suggest_name(title) index = 1 # Remove HTML entities and non-alphanumeric characters, replace spaces # with hyphens, and truncate the name at 64 characters. name = title.to_s.strip.downcase.gsub(/&[^\s;]+;/, '_'). gsub(/[^\s0-9a-z-]/, '').gsub(/\s+/, '-')[0..63] # Strip off any trailing non-alphanumeric characters. name.gsub!(/[_-]+$/, '') return '' if name.empty? # If the name consists solely of numeric characters, add an alpha # character to prevent name/id ambiguity. name += '_' unless name =~ /[a-z_-]/ # Ensure that the name doesn't conflict with any methods on the Post # controller and that no two posts have the same name. until self.name_unique?(name) if name[-1] == index name[-1] = (index += 1).to_s else name = name[0..62] if name.size >= 64 name += (index += 1).to_s end end return name end |
Instance Method Details
#atom_url ⇒ Object
Gets the Atom feed URL for this post.
134 135 136 |
# File 'lib/thoth/model/post.rb', line 134 def atom_url Config.site['url'].chomp('/') + PostController.r(:atom, name).to_s end |
#body=(body) ⇒ Object
138 139 140 141 |
# File 'lib/thoth/model/post.rb', line 138 def body=(body) self[:body] = body.strip self[:body_rendered] = RedCloth.new(wiki_to_html(body.dup.strip)).to_html end |
#comments ⇒ Object
Gets a dataset of visible comments attached to this post, ordered by creation time.
145 146 147 |
# File 'lib/thoth/model/post.rb', line 145 def comments @comments ||= Comment.filter(:post_id => id, :deleted => false).order(:created_at) end |
#created_at(format = nil) ⇒ Object
Gets the creation time of this post. If format is provided, the time will be returned as a formatted String. See Time.strftime for details.
151 152 153 154 155 156 157 |
# File 'lib/thoth/model/post.rb', line 151 def created_at(format = nil) if new? format ? Time.now.strftime(format) : Time.now else format ? self[:created_at].strftime(format) : self[:created_at] end end |
#name=(name) ⇒ Object
159 160 161 |
# File 'lib/thoth/model/post.rb', line 159 def name=(name) self[:name] = name.to_s.strip.downcase unless name.nil? end |
#tags ⇒ Object
Gets an Array of tags attached to this post, ordered by name.
164 165 166 167 168 169 170 |
# File 'lib/thoth/model/post.rb', line 164 def if new? || [] else ||= .all end end |
#tags=(tag_names) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/thoth/model/post.rb', line 172 def (tag_names) if tag_names.is_a?(String) tag_names = tag_names.split(',', 64) elsif !tag_names.is_a?(Array) raise ArgumentError, "Expected String or Array, got #{tag_names.class}" end tag_names = tag_names.map{|n| n.strip.downcase}.uniq.delete_if{|n| n.empty?} if new? # This Post hasn't been saved yet, so instead of attaching actual tags # to it, we'll create a bunch of fake tags just for the preview. We # won't create the real ones until the Post is saved. = [] tag_names.each {|name| << Tag.new(:name => name)} .sort! {|a, b| a.name <=> b.name } return else = [] # First delete any existing tag mappings for this post. TagsPostsMap.filter(:post_id => id).delete # Create new tags and new mappings. tag_names.each do |name| tag = Tag.find_or_create(:name => name) << tag TagsPostsMap.create(:post_id => id, :tag_id => tag.id) end return end end |
#title=(title) ⇒ Object
209 210 211 212 213 214 215 216 217 218 |
# File 'lib/thoth/model/post.rb', line 209 def title=(title) title.strip! # Set the post's name if it isn't already set. if self[:name].nil? || self[:name].empty? self[:name] = Post.suggest_name(title) end self[:title] = title end |
#updated_at(format = nil) ⇒ Object
Gets the time this post was last updated. If format is provided, the time will be returned as a formatted String. See Time.strftime for details.
223 224 225 226 227 228 229 |
# File 'lib/thoth/model/post.rb', line 223 def updated_at(format = nil) if new? format ? Time.now.strftime(format) : Time.now else format ? self[:updated_at].strftime(format) : self[:updated_at] end end |
#url ⇒ Object
Gets the URL for this post.
232 233 234 |
# File 'lib/thoth/model/post.rb', line 232 def url Config.site['url'].chomp('/') + PostController.r(:/, name).to_s end |
#validate ⇒ Object
236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/thoth/model/post.rb', line 236 def validate validates_presence(:name, :message => 'Please enter a name for this post.') validates_presence(:title, :message => 'Please enter a title for this post.') validates_presence(:body, :message => "What's the matter? Cat got your tongue?") validates_max_length(255, :title, :message => 'Please enter a title under 255 characters.') validates_max_length(64, :name, :message => 'Please enter a name under 64 characters.') validates_format(/^[0-9a-z_-]+$/i, :name, :message => 'Post names may only contain letters, numbers, underscores, and dashes.') validates_format(/[a-z_-]/i, :name, :message => 'Post names must contain at least one non-numeric character.') end |