NotiSearch Example App
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
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).
How it works
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, the
Search#clear_unsavedrecurring process takes care of deleting the old unsaved ones)
notify:booleanto switch on/off email notification of new results
notified_at:timestampwith the time of its last notification, so the
find_new_resultsprivate method can filter products based on whether they were created or updated after the last notification. See
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 purposes
new_results_checked_at:timestampholds the time reference of when the new results where found.
Search has also the following class methods:
Search.check_new_results_presencechecks new results presence to be notified only for searches with
Search.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 (
Search.clear_unsaveddeletes unsaved searches from database
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:
To get started
$ 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.
To run background and scheduled jobs
This 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
application.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.