Skip to content

Instantly share code, notes, and snippets.

@SteveOscar
Forked from joshfrench/article.rb
Last active April 14, 2016 01:44
Show Gist options
  • Save SteveOscar/28961b428c0714fafb169baf4ffe40b6 to your computer and use it in GitHub Desktop.
Save SteveOscar/28961b428c0714fafb169baf4ffe40b6 to your computer and use it in GitHub Desktop.
Upworthy written exercise: Steven Olson
## This implementation assumes that there is a method in this class that symbolizes Article attributes, as I don't think Rails allows a symbol datatype for model attributes. The other option would be to add '.to_sym' on the self attribute calls. This also assumes that the given combinaitons are the only ones that need to covered.
class Article
def combinations
{:assigned => [:approved, :rejected],
:review => [:approved, :rejected],
:approved => [:rejected],
:rejected => [:approved]}
end
def approved?(status_was, status)
if status == :followup
source_check?
else
combinations[status_was].include?(status) || review?
end
def review?
self.status_was != :review && self.status == :review
end
def source_check?
(self.article_source.nil? || self.article_source.try(:fact_checked?))
end
def send_email?
approved?(self.status_was, self.status) && self.status_was != :followup
end
end
class ArticleTest < ActiveSupport::TestCase
test "only sends appropriate articles" do
article = Article.create(status_was: 'assigned', status: 'approved')
assert article.send_email?
article.status_was = 'approved'
refute article.send_email?
##And so on to test all combinations, but I'm running out of time for this one.
end
end
// This assumes that there is an <h2 id='message'></h2> below the form and that jQuery is available, and that I should not modify the provided form's HTML.
function validateEmail(email) {
// Easy to swap out full RFC 2822 validation if desired
var emailCheck = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailCheck.test(email);
}
function validateMe(){
$("#message").text("");
var email = $("#email-field").val();
if (validateEmail(email)) {
$("#message").text(email + " is valid");
} else {
$("#message").text(email + "is NOT valid");
}
return false;
}
$("form").bind("submit", validateMe);
//Sample Test
// Assumes presence of selenium-webdriver and a Rails/postgres backend
class ArticleSeleniumTest < ActionDispatch::IntegrationTest
test "enter invalid email" do
driver = Selenium::WebDriver.for:firefox
driver.navigate.to "some_relevant_URL"
element = driver.find_element(:id, 'email-field')
element.send_keys "bad_email"
driver.find_element(:id, 'email-submit').click
assert driver.find_element(:id=>"message").text.include? "is NOT valid"
end
test "enter valid email" do
driver = Selenium::WebDriver.for:firefox
driver.navigate.to "some_relevant_URL"
element = driver.find_element(:id, 'email-field')
element.send_keys "challenge@upworthy.com"
driver.find_element(:id, 'email-submit').click
assert driver.find_element(:id=>"message").text.include? "is valid"
end
end

(written assuming that no outside services should be used, so no Solr or Elastic Search)

Full Text Search

pre-research:

Just quickly - how I would NOT implement text search in Rails: using Active Record/SQL queries to comb the DB Articles for matches. That's expensive and slow.

My initial (pre-research) response for how to implement a custom text search feature would be to use a trie. One of my past Ruby projects was an autocomplete program using an n-array trie that was loaded with the full dictionary (each node was a letter and contained a flag to indicate the end of a word). Of course, for a text search we would need to search Upworthy's content for strings matching the input instead of suggesting words.

The way I imagine this working is, as articles are loaded into the trie, the node that completes each word is flagged with the article's ID. Words in an article headline could use an additional flag that carries more weight, as having 'cat' in the headline suggests the article is about cats, vs coincidentally being mentioned in the text of an article that may really be about something else.

A submitted search term could be fed into the trie, which would return all article ID's that are flagged at that point. The results could be sorted by amount/weight of flags associated with each article. The response would be quick, even if a massive amount of articles were loaded into the trie, as the growth of complexity in the trie slows over time (but article ID flags would accumulate linearly). It would be helpful to filter out low-value stops words that are found in a blacklist (array, regex, etc.).

post-research:

After researching the topic of text-search data structures, a trie still seems like a good choice. There are more specialized versions that would be more efficient, like a suffix trie, which compresses single nodes, instead storing them as index ranges in one node. An inverted index could be used to map words to articles, which helps with prefix searching.

Given this one hour of time to write/research, I would aim to implement a trie structure, which I'm already familiar with, but to increase performance by learning how to compress nodes and use an inverted index (two things I've not implemented before). If a synonym search was required, I would imagine using a gem (Dinosaurus) or API to grab synonyms and perform text searches in the trie on those terms as well (although a different data structure might be better suited if synonym searching is required).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment