DEPRECATED: It's been 8 years now, plase don't use code inspired by this repository in production. Be aware that I was a junior developer when I wrote this :)
This Example App allows its users to subscribe to their advanced searches to receive new search results via email.
Users of this example app can perform and save advanced Searches on Products. Than they can subscribe to their preferred (saved) searches in order to receive an email notification when new search results are available for that particular search (like on Yahoo answers).
The basic idea behind user-preferred searches new results is that the Search model has also the following attributes:
saved:booleanto save the search to prevent its deletion (since advanced searches are models backed by the DB, theSearch#clear_unsavedrecurring process takes care of deleting the old unsaved ones)notify:booleanto switch on/off email notification of new resultsnotified_at:timestampwith the time of its last notification, so thefind_new_resultsprivate method can filter products based on whether they were created or updated after the last notification. SeeSearch#new_results;new_results_presence:booleanis a flag set to true whenever new results are found for the Search, only searches with this flag set to true are processed for mail notification purposesnew_results_checked_at:timestampholds the time reference of when the new results where found.
And then Search has also the following class methods:
Search.check_new_results_presencechecks new results presence to be notified only for searches withnotify: trueandnew_results_presence: falseSearch.notify_new_results_by_mailfetches users with searches with new_results to be notified and then, for each of them, it calls the notification mailer (UserMailer#new_search_results_for(user))Search.clear_unsaveddeletes unsaved searches from database
Each one of these tree class methods are run periodically by Whenever (see schedule.rb) in background jobs handled by Delayed_job.
Last but not least UserMailer#new_search_results_for(user) mailer fetches all new search results for every search of the passed user, collect them in a @new_result_sets hash (in which the searches are the keys and the related new results are the values) and then touches the notified_at attribute for every search in it. The email template will then use @new_result_sets to generate the email body.
The most important pieces of code were BDD, so you can get a glance of what they do also reading the following specs:
$ bundle install$ rails generate delayed_job:active_record$ rake db:migrate$ cp config/application.example.yml config/application.yml$ cp config/database.example.yml config/database.yml
To trigger a new results email notification you must first save a search for a user, then activate the notification for that search and then create or update a matching product.
$ wheneverThis will simply show you your schedule.rb file converted to cron syntax. It does not read or write your crontab file. Run whenever --help for a complete list of options.
- Run whenever in development mode
$ whenever --set environment=development -w - See crontab
$ crontab -l - Clear crontab in development mode
$ whenever --set environment=development -c - Start background jobs
$ rake jobs:work
- There are no navigation links yet, so you should navigate through resources using the address bar and RESTful actions, see routes.rb
- Searches#index shows only the searches of the current user (this isn't ideal but was the simplest solution)
- There is no authorization logic because isn't needed in an example app
- It relies on PostgreSQL for full-text search.
- This example app is.. well, just an example app, still in development (see DevLog.md), for a real production app you should consider a lot of performance optimization and be ready to scale to a solution based, for instance, on Resque Scheduler which has Redis as an external dependency.
- Furthermore you should consider all the issues related to mass emailing, for instance Gmail is good for up to 200 emails/day (this is the reason for the
max_daily_emails: 200inapplication.example.yml). If you need to scale, possible alternatives to Gmail are mailjet, sendgrid, and mailchimp.
- Users development is based on Rails Tutorial by Michael Hartl
- User remember me and reset password based on Railscast #275
- Advanced Searches based on Railscast #111
- Newsletter about new results in user-preferred searches by Duccio Armenise :-).
- Ryan Bates for replying with useful hints to my email and, of course, for running Railscasts.com.
- Leo Correa for his answer on Stackoverflow.