Skip to content

Commit

Permalink
refactor: improve readability of docs and code performance
Browse files Browse the repository at this point in the history
  • Loading branch information
gildesmarais committed Aug 9, 2024
1 parent 4a24997 commit 711ded6
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 91 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ Metrics/BlockLength:
- Rakefile
ExcludedMethods:
- route

Naming/RescuedExceptionsVariableName:
PreferredName: error
97 changes: 55 additions & 42 deletions app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,70 +45,83 @@ class App < Roda
'X-XSS-Protection' => '1; mode=block'

plugin :error_handler do |error|
handle_error(error)
end

plugin :public
plugin :render, escape: true, layout: 'layout'
plugin :typecast_params
plugin :basic_auth

route do |r|
path = RequestPath.new(request)

r.root { view 'index' }

r.public

r.get 'health_check.txt' do
handle_health_check
end

r.on String do |config_name_with_ext|
handle_local_config_feeds(path.full_config_name, config_name_with_ext)
end

r.on String, String do |folder_name, config_name_with_ext|
handle_html2rss_configs(path.full_config_name, folder_name, config_name_with_ext)
end
end

private

def handle_error(error) # rubocop:disable Metrics/MethodLength
case error
when Html2rss::Config::ParamsMissing,
Roda::RodaPlugins::TypecastParams::Error
@page_title = 'Parameters missing or invalid'
response.status = 422
set_error_response('Parameters missing or invalid', 422)
when Html2rss::AttributePostProcessors::UnknownPostProcessorName,
Html2rss::ItemExtractors::UnknownExtractorName,
Html2rss::Config::ChannelMissing
@page_title = 'Invalid feed config'
response.status = 422
set_error_response('Invalid feed config', 422)
when ::App::LocalConfig::NotFound,
Html2rss::Configs::ConfigNotFound
@page_title = 'Feed config not found'
response.status = 404
set_error_response('Feed config not found', 404)
else
@page_title = 'Internal Server Error'
response.status = 500
set_error_response('Internal Server Error', 500)
end

@show_backtrace = ENV.fetch('RACK_ENV', nil) == 'development'
@error = error
view 'error'
end

plugin :public
plugin :render, escape: true, layout: 'layout'
plugin :typecast_params
plugin :basic_auth

route do |r|
path = RequestPath.new(request)

r.root do
view 'index'
end

r.public
def set_error_response(page_title, status)
@page_title = page_title
response.status = status
end

r.get 'health_check.txt' do |_|
HttpCache.expires_now(response)
def handle_health_check
HttpCache.expires_now(response)

with_basic_auth(realm: HealthCheck,
username: HealthCheck::Auth.username,
password: HealthCheck::Auth.password) do
HealthCheck.run
end
with_basic_auth(realm: HealthCheck,
username: HealthCheck::Auth.username,
password: HealthCheck::Auth.password) do
HealthCheck.run
end
end

# Route for feeds from the local feeds.yml
r.get String do |_config_name_with_ext|
Html2rssFacade.from_local_config(path.full_config_name, typecast_params) do |config|
response['Content-Type'] = 'text/xml'

HttpCache.expires(response, config.ttl * 60, cache_control: 'public')
end
def handle_local_config_feeds(full_config_name, _config_name_with_ext)
Html2rssFacade.from_local_config(full_config_name, typecast_params) do |config|
response['Content-Type'] = 'text/xml'
HttpCache.expires(response, config.ttl * 60, cache_control: 'public')
end
end

# Route for feeds from html2rss-configs
r.get String, String do |_folder_name, _config_name_with_ext|
Html2rssFacade.from_config(path.full_config_name, typecast_params) do |config|
response['Content-Type'] = 'text/xml'

HttpCache.expires(response, config.ttl * 60, cache_control: 'public')
end
def handle_html2rss_configs(full_config_name, _folder_name, _config_name_with_ext)
Html2rssFacade.from_config(full_config_name, typecast_params) do |config|
response['Content-Type'] = 'text/xml'
HttpCache.expires(response, config.ttl * 60, cache_control: 'public')
end
end
end
Expand Down
43 changes: 24 additions & 19 deletions app/health_check.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# frozen_string_literal: true

require 'parallel'

require_relative 'local_config'
require 'securerandom'

module App
##
Expand All @@ -11,18 +11,22 @@ module HealthCheck
##
# Contains logic to obtain username and password to be used with HealthCheck endpoint.
class Auth
def self.username
@username ||= ENV.delete('HEALTH_CHECK_USERNAME') do
SecureRandom.base64(32).tap do |string|
puts "HEALTH_CHECK_USERNAME env var. missing! Please set it. Using generated value instead: #{string}"
end
class << self
def username
@username ||= fetch_credential('HEALTH_CHECK_USERNAME')
end
end

def self.password
@password ||= ENV.delete('HEALTH_CHECK_PASSWORD') do
SecureRandom.base64(32).tap do |string|
puts "HEALTH_CHECK_PASSWORD env var. missing! Please set it. Using generated value instead: #{string}"
def password
@password ||= fetch_credential('HEALTH_CHECK_PASSWORD')
end

private

def fetch_credential(env_var)
ENV.delete(env_var) do
SecureRandom.base64(32).tap do |string|
warn "ENV var. #{env_var} missing! Using generated value instead: #{string}"
end
end
end
end
Expand All @@ -34,12 +38,7 @@ def self.password
# @return [String] "success" when all checks passed.
def run
broken_feeds = errors

if broken_feeds.any?
broken_feeds.join("\n")
else
'success'
end
broken_feeds.any? ? broken_feeds.join("\n") : 'success'
end

##
Expand All @@ -48,10 +47,16 @@ def errors
[].tap do |errors|
Parallel.each(LocalConfig.feed_names, in_threads: 4) do |feed_name|
Html2rss.feed_from_yaml_config(LocalConfig::CONFIG_FILE, feed_name.to_s).to_s
rescue StandardError => e
errors << "[#{feed_name}] #{e.class}: #{e.message}"
rescue StandardError => error
errors << "[#{feed_name}] #{error.class}: #{error.message}"
end
end
end

def format_error(feed_name, error)
"[#{feed_name}] #{error.class}: #{error.message}"
end

private_class_method :format_error
end
end
20 changes: 9 additions & 11 deletions app/html2rss_facade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,47 @@ class Html2rssFacade

##
# @param feed_config [Hash<Symbol, Object>]
# @param typecast_params
# @param typecast_params [Object]
def initialize(feed_config, typecast_params)
@feed_config = feed_config
@typecast_params = typecast_params
end

##
# @param name [String] the name of a html2rss-configs provided config.
# @param typecast_params
# @return [String] the serializied RSS feed
# @param typecast_params [Object]
# @return [String] the serialized RSS feed
def self.from_config(name, typecast_params, &)
feed_config = Html2rss::Configs.find_by_name(name)

new(feed_config, typecast_params).feed(&)
end

##
# @param name [String] the name of a feed in the file `config/feeds.yml`
# @param typecast_params
# @return [String] the serializied RSS feed
# @param typecast_params [Object]
# @return [String] the serialized RSS feed
def self.from_local_config(name, typecast_params, &)
feed_config = LocalConfig.find name

feed_config = LocalConfig.find(name)
new(feed_config, typecast_params).feed(&)
end

##
# @return [String]
def feed
config = self.class.feed_config_to_config(feed_config, typecast_params)

yield config if block_given?

Html2rss.feed(config).to_s
end

##
# @param feed_config [Hash<Symbol, Object>]
# @param typecast_params [Object]
# @param global_config [Hash<Symbol, Object>]
# @return [Html2rss::Config]
# @raise [Roda::RodaPlugins::TypecastParams::Error]
def self.feed_config_to_config(feed_config, typecast_params, global_config: LocalConfig.global)
dynamic_params = Html2rss::Config::Channel.required_params_for_config(feed_config[:channel])
.to_h { |name| [name, typecast_params.str!(name)] }

Html2rss::Config.new(feed_config, global_config, dynamic_params)
end
end
Expand Down
14 changes: 6 additions & 8 deletions app/http_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,21 @@ module HttpCache

##
# Sets Expires and Cache-Control headers to cache for `seconds`.
# @param response [#[]]
# @param response [Hash]
# @param seconds [Integer]
# @param cache_control [String]
# @param cache_control [String, nil]
def expires(response, seconds, cache_control: nil)
response['Expires'] = (Time.now + seconds).httpdate

response['Cache-Control'] = if cache_control
"max-age=#{seconds},#{cache_control}"
else
"max-age=#{seconds}"
end
cache_value = "max-age=#{seconds}"
cache_value += ",#{cache_control}" if cache_control
response['Cache-Control'] = cache_value
end

##
# Sets Expires and Cache-Control headers to invalidate existing cache and
# prevent caching.
# @param response [#[]]
# @param response [Hash]
def expires_now(response)
response['Expires'] = '0'
response['Cache-Control'] = 'private,max-age=0,no-cache,no-store,must-revalidate'
Expand Down
17 changes: 10 additions & 7 deletions app/local_config.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'yaml'

module App
##
# Provides helper methods to deal with the local config file at `CONFIG_FILE`.
Expand All @@ -15,19 +16,19 @@ class NotFound < RuntimeError; end

##
# @param name [String, Symbol, #to_sym]
# @return [Hash<Symbol, Hash>]
# @return [Hash<Symbol, Any>]
def find(name)
feeds&.fetch(name.to_sym, false) || raise(NotFound, "Did not find local feed config at '#{name}'")
feeds.fetch(name.to_sym) { raise NotFound, "Did not find local feed config at '#{name}'" }
end

##
# @return [Hash<Symbol, Hash>]
# @return [Hash<Symbol, Any>]
def feeds
yaml[:feeds] || {}
yaml.fetch(:feeds, {})
end

##
# @return [Hash<Symbol, Hash>]
# @return [Hash<Symbol, Any>]
def global
yaml.reject { |key| key == :feeds }
end
Expand All @@ -39,9 +40,11 @@ def feed_names
end

##
# @return [Hash<Symbol, Hash>]
# @return [Hash<Symbol, Any>]
def yaml
YAML.safe_load(File.open(CONFIG_FILE), symbolize_names: true).freeze
YAML.safe_load_file(CONFIG_FILE, symbolize_names: true).freeze
rescue Errno::ENOENT => error
raise NotFound, "Configuration file not found: #{error.message}"
end
end
end
8 changes: 4 additions & 4 deletions app/request_path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class RequestPath
# @param request [Rack::Request, #path]
def initialize(request)
@full_path = request.path[1..]
parts = @full_path.split('/')

if @full_path.count('/').zero?
if parts.size == 1
@name_with_ext = @full_path
else
parts = @full_path.split('/')
@folder_name = parts[0..-2]
@name_with_ext = parts[-1]
end
Expand All @@ -23,13 +23,13 @@ def initialize(request)
##
# @return [String]
def full_config_name
[folder_name, config_name].compact.join('/')
[@folder_name, config_name].compact.join('/')
end

##
# @return [String]
def config_name
parts[...-1].join('.')
parts[..-2].join('.')
end

##
Expand Down

0 comments on commit 711ded6

Please sign in to comment.