validates :post_type, presence: true, inclusion: { in: %w[dispatch bookmark] }
validates :title, presence: true
- validates :slug, presence: true, uniqueness: true, if: :dispatch?
+ validates :slug, uniqueness: true, allow_nil: true
validates :published_at, presence: true, if: :dispatch?
validates :content, presence: true, if: :dispatch?
validates :url, presence: true, if: :bookmark?
before_validation :set_slug, if: :new_record?
before_validation :set_title_from_url, if: -> { bookmark? && title.blank? }
+ after_create :ensure_slug_present
+ after_update :ensure_slug_present
scope :dispatches, -> { where(post_type: 'dispatch') }
scope :bookmarks, -> { where(post_type: 'bookmark') }
def self.get_posts_and_bookmarks_with_pagination(page, per_page, filter)
posts = case filter
- when 'posts'
+ when 'posts'
dispatches
- when 'bookmarks'
+ when 'bookmarks'
bookmarks
- else
+ else
all
- end
+ end
posts.order(published_at: :desc).page(page).per(per_page)
end
tags.split(/\s*(?:,|\s+and)\s*/).map { |tag| "<code>#{tag.strip}</code>" }.join(', ') + '.'
end
- def generate_excerpt(max_length = 1040, take = 10)
+ def generate_excerpt(max_length = 210)
return "" if content.blank?
stripped_content = ActionController::Base.helpers.strip_tags(content)
- excerpt = stripped_content.split('.') if stripped_content.split('.')
- excerpt = excerpt.take(take)
- excerpt = excerpt.join('.')
+ excerpt = stripped_content.split('.').first || stripped_content[0...max_length]
excerpt = excerpt[0...max_length] if excerpt.length > max_length
excerpt.gsub!("Dear friends,", "")
+ excerpt.gsub!(/\s+/, ' ')
excerpt.strip
end
private
def set_slug
- self.slug = title.present? ? title.parameterize : nil
+ return if slug.present?
+
+ if title.present?
+ self.slug = title.parameterize
+ elsif bookmark?
+ # For bookmarks without a title, create a slug based on the URL's domain
+ uri = URI.parse(url)
+ domain_slug = uri.host.gsub(/^www\./, '').parameterize
+ timestamp = Time.current.strftime('%Y%m%d%H%M%S')
+ self.slug = "#{domain_slug}-#{timestamp}"
+ end
+ rescue URI::InvalidURIError
+ # If URL parsing fails, fall back to UUID
+ self.slug = "bookmark-#{SecureRandom.uuid}"
end
def set_published_at
self.published_at ||= Time.current
end
- private
-
def set_title_from_url
return unless bookmark? && url.present? && title.blank?
self.title = "Bookmark"
end
end
+
+ def ensure_slug_present
+ return if slug.present?
+
+ # Generate a fallback slug if none exists
+ fallback_slug = if title.present?
+ base_slug = title.parameterize
+ slug = base_slug
+ counter = 1
+ # Ensure uniqueness
+ while Post.where.not(id: id).exists?(slug: slug)
+ slug = "#{base_slug}-#{counter}"
+ counter += 1
+ end
+ slug
+ else
+ "bookmark-#{SecureRandom.uuid}"
+ end
+
+ # Use update_column to skip validations and callbacks
+ update_column(:slug, fallback_slug)
+ end
end
<p ><% if
[email protected]_dispatch?%>About a <%= @reading_time %> minute read, p<% else %>P<% end %>osted <%= @post.published_at.strftime('%B %d, %Y') %> and tagged <%= raw @post.format_tags %></p>
</div>
<% else %>
- <h1 style="margin-bottom: -.8em">Bookmark comment permalink:</h1>
- <h3><%= @post.title %></h3>
+<h3 class="post_siteheading">Aidan Cornelius-Bellโs mind reader:</h3>
+<h1 class="post_title"><%= @post.title %></h1>
<div class="bookmark-buttons">
<%= link_to "๐ View linked content", @post.url, class: "button button-bottomless", target: "_BLANK" %>
<%= link_to "๐ More from mind reader", "#{root_path}", class: "button button-bottomless" %>
</div>
<div class="postmeta">
- <p>Originally bookmarked on mind reader on <%= @post.published_at.strftime('%B %d, %Y') %>.</p>
+ <p>A link to third party content first shared on mind reader on <%= @post.published_at.strftime('%B %d, %Y') %>.</p>
</div>
<% end %>
</div>
<% end %>
<%= raw @rendered_content %>
- <%= render partial: "layouts/post_navigation" %>
- <hr class="foot_separator">
+ <% if @post.post_type != "bookmark" %>
+ <%= render partial: "layouts/post_navigation" %>
+ <hr class="foot_separator">
+ <% end %>
</div>
</div>