Skip to content

Instantly share code, notes, and snippets.

@rondy
Last active March 2, 2021 11:14
Show Gist options
  • Save rondy/a9f167cca216dfb51d1dc1fac56755b1 to your computer and use it in GitHub Desktop.
Save rondy/a9f167cca216dfb51d1dc1fac56755b1 to your computer and use it in GitHub Desktop.
class ReviseElixirRadarEntry
def call(entry)
result = Validation() do
check { check_url_points_to_an_existing_page(entry[:url]) }
check { check_domain_matches_url_host(entry[:domain], entry[:url]) }
check { check_entry_title_matches_page_title(entry[:url], entry[:title]) }
check { check_utm_campaign_is_valid(entry[:url]) }
end
review_result = {
title: entry[:title]
}
case result
when Success
review_result[:status] = 'valid'
when Failure
review_result[:status] = 'invalid'
review_result[:errors] = result
end
review_result
end
private
def check_url_points_to_an_existing_page(url)
if CheckUrlPointsToAnExistingPage.new.call(url)
Success(url)
else
Failure('page_not_found')
end
end
def check_domain_matches_url_host(domain, url)
# case CheckDomainMatchesUrlHost.new.call(domain, url)
# when true then Success(url)
# when false then Failure('domain_does_not_match')
# end
if CheckDomainMatchesUrlHost.new.call(domain, url)
Success(url)
else
Failure('domain_does_not_match')
end
end
def check_entry_title_matches_page_title(url, entry_title)
if CheckEntryTitleMatchesPageTitle.new.call(url, entry_title)
Success(url)
else
Failure('page_title_does_not_match')
end
end
def check_utm_campaign_is_valid(url)
if CheckUtmCampaignIsValid.new.call(url)
Success(url)
else
Failure('wrong_utm_campaign')
end
end
end
class CheckEntryTitleMatchesPageTitle
def initialize(get_page_title: nil)
@get_page_title = get_page_title
end
def call(url, entry_title)
page_title = get_page_title.call(url)
normalized_page_title =
normalize_page_title(page_title)
entry_title_matches_page_title =
entry_title_matches_page_title?(normalized_page_title, entry_title)
[entry_title_matches_page_title, normalized_page_title]
end
private
def normalize_page_title(page_title)
page_title
.gsub(/\s+/, ' ')
.strip
end
def get_page_title(url)
@get_page_title ||= begin
lambda do |url|
GetPageTitle
.new(with: :mechanize)
.call(url)
end
end
end
def entry_title_matches_page_title?(normalized_page_title, entry_title)
(normalized_page_title =~ Regexp.new(entry_title.strip))
end
end
class GetPageTitle
STRATEGIES = {
mechanize: lambda do |url|
Mechanize.new.get(url).title
end
}
def initialize(with: strategy)
@strategy = strategy
end
def call(url)
STRATEGIES.fetch[@strategy].call(url)
end
end
CheckEntryTitleMatchesPageTitle
.new(
get_page_title: lambda do |url|
'Elixir/Erlang Clustering in Kubernetes'
end
)
.call(
url: 'http://bitwalker.org/posts/2016-08-04-clustering-in-kubernetes',
entry_title: 'Elixir/Erlang Clustering in Kubernetes'
)
# => [true, 'Elixir/Erlang Clustering in Kubernetes']
class CheckUtmCampaignIsValid
def initialize(current_issue_number: nil)
@current_issue_number = current_issue_number
end
def call(url)
[
is_utm_campaign_valid?(extract_utm_campaign_value_from(url)),
given_utm_campaign_value
]
end
private
def is_utm_campaign_valid?(given_utm_campaign_value)
(given_utm_campaign_value == current_issue_number.call)
end
def extract_utm_campaign_value_from(url)
parse_query_strings(url)
.fetch('utm_campaign', [])
.first
end
def parse_query_strings(url)
CGI.parse(URI.parse(url).query)
end
def current_issue_number
@current_issue_number ||= lambda { $current_issue_number }
end
end

Functional objects on Ruby programming language

  • Start class names with a verb;
  • Public contract is a #call method;
    • Enables SRP;
    • Enables composition;
    • Enables polymorphism;
    • Enables to receive Proc/callables objects;
  • Receive stateful/impure functions through the object initializer;
    • This permits easy mocking/substitution.
  • Receive pure function inputs through the #call method;
  • This resembles curry-like functions;
  • The #call calling always returns a value, preferably 'result'-like objects;
    • This avoids 'primitive obssesion' anti-pattern;
    • Consider monadics operations;
  • Define stateful/impure functions as default implementation, in case nothing is received;
  • Stateful/impure functions are always returned as closures/lambda;
  • Object state is uded only for atateful/impure collaborators.
    extract_utm_campaign_value_from(url) # is pure
    get_page_title.call(url)             # is impure
  • Message passing is natural;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment