Class: Feed

Inherits:
ApplicationRecord show all
Defined in:
app/models/feed.rb

Overview

Feed model. Each instance of this model represents a single feed (Atom, RSS…) to which users can be suscribed.

A single feed can have many subscriptions (from different users), but a single subscription corresponds to a single feed (one-to-many relationship).

Users are associated with Feeds through the FeedSubscription model. This enables us to retrieve users that are subscribed to a feed.

Feeds can be associated with folders. Each feed can be in many folders (as long as they belong to different users), and each folder can have many feeds (many-to-many association). However a single feed cannot be associated with more than one folder from the same user.

Each feed can have many entries.

Each feed can have many deleted_entries

Each feed can be associated with many refresh_feed_job_states. Each such association represents an ocurrence of a user manually requesting a refresh of this feed.

Each feed can be associated with many subscribe_job_states. Each such association represents an occurrence of a user successfully subscribing to the feed. They are transient and can be destroyed if the user dismisses the alert that informs him of the success subscribing to the feed; but if there is still a FeedSusbscription instance joining user and feed, the user is still subscribed to the feed.

Each feed, identified by its fetch_url, can be present at most once in the database. Different feeds can have the same title, as long as they have different fetch_url.

Attributes of the model:

  • title

  • fetch_url (URL to fetch the feed XML)

  • last_fetched (timestamp of the last time the feed was fetched, nil if it's never been fetched)

  • fetch_interval_secs (current interval between fetches, in seconds)

  • failing_since (if not null, feed updates have been failing since the datetime value of this field)

  • available (if false, the feed is permanently unavailable and updates are not scheduled for it)

  • url (URL to which the user will be linked; usually the website that originated this feed)

Title, fetch_url and url are sanitized (with ActionView::Helpers::SanitizeHelper) before validation; this is, before saving/updating each instance in the database.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.url_variants_feed(feed_url) ⇒ Object

Check if a feed exists in the database matching a given URL. This is a class method.

Receives as argument a URL.

It checks several variants of the passed URL to see if there's a matching feed:

  • First it checks with the passed URL as-is.

  • If the passed URL has a trailing slash, it checks with the slash removed.

  • If the passed URL does not have a trailing slash, it checks with an added trailing slash.

In all three cases it invokes the Feed.url_feed method to check if there's a matching feed.

If a matching feed is found, it is returned. Otherwise returns nil.


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'app/models/feed.rb', line 128

def self.url_variants_feed(feed_url)
  # Ensure that the passed url has an http:/// or https:// uri-scheme
  url = URLNormalizer.normalize_feed_url feed_url
  # Remove leading and trailing whitespace, to avoid confusion when detecting trailing slashes
  stripped_url = url.strip
  Rails.logger.info "Searching for matching feeds for url #{stripped_url}"
  matching_feed = find_feed_by_url stripped_url
  if matching_feed.blank? && stripped_url =~ /.*[^\/]$/
    Rails.logger.info "No matching feed found for #{stripped_url}, adding trailing slash to search again for url"
    url_slash = stripped_url + '/'
    matching_feed = find_feed_by_url url_slash
  elsif matching_feed.blank? && stripped_url =~ /.*\/$/
    Rails.logger.info "No matching feed found for #{stripped_url}, removing trailing slash to search again for url"
    url_no_slash = stripped_url.chop
    matching_feed = find_feed_by_url url_no_slash
  end

  return matching_feed
end

Instance Method Details

#remove_from_folder(user) ⇒ Object

Remove this feed from its current folder, if any, for a given user.

Receives as argument a user.

A feed can only be in a single folder owned by a given user, so it's not necessary to pass the folder id as an argument, it can be inferred from the user id and feed id.

If the feed is in a folder owned by the passed user, it is removed from the folder. Otherwise nothing is done.

Returns a Folder instance with the data of the folder in which the feed was previously, or nil if it wasn't in any folder. This object may have already been deleted from the database, if there were no more feeds in it.


101
102
103
104
105
106
107
108
109
110
111
# File 'app/models/feed.rb', line 101

def remove_from_folder(user)
  folder = self.user_folder user
  if folder.present?
    Rails.logger.info "user #{user.id} - #{user.email} is removing feed #{self.id} - #{self.fetch_url} from folder #{folder.id} - #{folder.title}"
    folder.feeds.delete self
  else
    Rails.logger.info "user #{user.id} - #{user.email} is trying to remove feed #{self.id} - #{self.fetch_url} from its folder, but it's not in any folder"
  end

  return folder
end

#user_folder(user) ⇒ Object

Find the folder to which a feed belongs, for a given user.

Receives as argument a user.

A feed can belong to many folders that belong to many users, but only to a single folder for a given user. This method searches among the folders to which this feed belongs, trying to find one that belongs to the user passed as argument.

If a matching folder is found, it is returned. Otherwise nil is returned.


81
82
83
84
# File 'app/models/feed.rb', line 81

def user_folder(user)
  folder = self.folders.find_by user_id: user.id
  return folder
end