Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor mini-tools #134

Merged
merged 75 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
66f7182
Clean up seeds
josefarias Sep 7, 2024
13e080c
Refactor financial freedom calculator
josefarias Sep 7, 2024
b2183c7
Fix double rendering of bar chart when navigating back
josefarias Sep 8, 2024
7dade11
Refactor roi calculator
josefarias Sep 8, 2024
d925804
widgets/charts -> widgets/content
josefarias Sep 8, 2024
e7f101b
source -> active_record
josefarias Sep 8, 2024
dc61ede
source -> _
josefarias Sep 8, 2024
3554130
Refactor loan calculator
josefarias Sep 8, 2024
c3942c4
Refactor inflation calculator
josefarias Sep 8, 2024
2b0ee73
Use shared blank slate and fix copy
josefarias Sep 8, 2024
8db2659
Fix blank slate partial path
josefarias Sep 8, 2024
9651f8c
savings_by_month -> monthly_savings
josefarias Sep 8, 2024
15cfe9e
Refactor compound interest calculator
josefarias Sep 8, 2024
6f5f27e
Refactor 401k retirement calculator
josefarias Sep 8, 2024
06b35fb
Pull out option extraction methods
josefarias Sep 8, 2024
f19f56e
Refactor bogleheads
josefarias Sep 9, 2024
500c1e3
Comment `#fetch_mortgage_rate`
josefarias Sep 9, 2024
7115d42
Explain `#transform_values`
josefarias Sep 9, 2024
fde88e9
Merge branch 'main' into refactor-mini-tools
josefarias Sep 14, 2024
b2e3ba3
Simplify presenter signatures
josefarias Sep 14, 2024
c0cd925
Fix #unit_field formatting
josefarias Sep 15, 2024
df67448
Fix retirement calculator slug
josefarias Sep 15, 2024
dd9d921
Use floats instead of decimals
josefarias Sep 15, 2024
75e7c2b
Refactor home affordability calculator
josefarias Sep 15, 2024
9d2c082
Pull out active_record method
josefarias Sep 15, 2024
0094597
Index stock_prices on ticker
josefarias Sep 15, 2024
776109f
Refactor early mortgage payoff calculator
josefarias Sep 15, 2024
84e8909
Clean up #months_to_payoff_with_extra_payments
josefarias Sep 15, 2024
7a0dfab
Remove unnecessary data-controller attrs
josefarias Sep 15, 2024
02edfce
Clean up select markup
josefarias Sep 15, 2024
0f56137
Use double quotes
josefarias Sep 15, 2024
ca513a4
Clean up `_period_select` markup
josefarias Sep 15, 2024
b920f19
Bring back synchronized-input for bogleheads
josefarias Sep 15, 2024
2e837d5
Use `ActiveModel::Attributes` API
josefarias Sep 15, 2024
840e440
Use `#delete_prefix`
josefarias Sep 15, 2024
1a4f8de
Pass frame_id as local
josefarias Sep 15, 2024
aa41167
Use Tool::Presenter namespace
josefarias Sep 23, 2024
c415a66
series_data -> legend data
josefarias Sep 23, 2024
790be43
Refactor Stock Portfolio Backtest
josefarias Sep 23, 2024
b502733
Stock Portfolio Backtest formatting fixes
josefarias Sep 23, 2024
4507325
Roll back superficial JS changes
josefarias Sep 23, 2024
0bc4408
Clean up full ticker names
josefarias Sep 24, 2024
4cff53d
Pull out MortgageRate::Cache
josefarias Sep 24, 2024
5ac39cb
Use tool attributes as default values
josefarias Sep 24, 2024
6e62d96
Silence `to_time` deprecation warning
josefarias Sep 24, 2024
46f7776
Implement provider tests
josefarias Sep 24, 2024
6dac5f1
Clean up fixtures
josefarias Sep 24, 2024
8030ebe
Test tools controller
josefarias Sep 24, 2024
62b86f1
Test BogleheadsGrowthCalculator
josefarias Sep 24, 2024
b8dedd7
Test CompoundInterestCalculator
josefarias Sep 25, 2024
463d74c
Add first plot point to bogleheads test
josefarias Sep 25, 2024
878aba3
Use strings for CompoundInterestCalculator test
josefarias Sep 25, 2024
b26991c
Test EarlyMortgagePayoffCalculator
josefarias Sep 25, 2024
f0d01b2
Add missing blankness tests
josefarias Sep 25, 2024
9bda7fe
Test FinancialFreedomCalculator
josefarias Sep 25, 2024
77ea2e7
Test HomeAffordabilityCalculator
josefarias Sep 25, 2024
0fe4cc2
Test InflationCalculator
josefarias Sep 25, 2024
37b9feb
Test LoanCalculator
josefarias Sep 25, 2024
da1be29
Test RetirementCalculator
josefarias Sep 25, 2024
4eebc39
Test RoiCalculator
josefarias Sep 25, 2024
7c1e1ba
Don't match api_key on Fred VCR requests
josefarias Sep 30, 2024
3951128
Also ignore Fred api key on mortgage rate cage test
josefarias Sep 30, 2024
a0aba24
Roll back superficial JS changes
josefarias Sep 30, 2024
d2b92ee
Remove unnecessary freezing
josefarias Sep 30, 2024
3e62844
Test StockPortfolioBacktest
josefarias Sep 30, 2024
1047527
Use more stocks in portfolio backtest tets
josefarias Sep 30, 2024
edc6624
Require portfolio backtest dates
josefarias Sep 30, 2024
0182724
Add limits to retirement calculator ages
josefarias Sep 30, 2024
c44535e
Split out the same month guard in StockPortfolioBacktest
josefarias Sep 30, 2024
58da942
seeds/seeds -> seeds/tools
josefarias Sep 30, 2024
348ae49
Limit Provider::Synth#stock_price API call to 1 result
josefarias Oct 1, 2024
f699774
Update stock prices for all existing tickers
josefarias Oct 1, 2024
17b3235
Bump allowed resultset size for FinancialFreedomCalculator
josefarias Oct 1, 2024
19c3169
Introduce some more guards
josefarias Oct 1, 2024
3ae04bd
Fix failing tests
josefarias Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,7 @@ group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
gem "selenium-webdriver"
gem "mocha"
gem "vcr"
gem "webmock"
end
16 changes: 16 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ GEM
xpath (~> 3.2)
concurrent-ruby (1.3.4)
connection_pool (2.4.1)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
csv (3.3.0)
date (3.3.4)
Expand Down Expand Up @@ -192,6 +195,7 @@ GEM
ffi (1.17.0-x86_64-linux-gnu)
globalid (1.2.1)
activesupport (>= 6.1)
hashdiff (1.1.1)
hotwire-livereload (1.4.1)
actioncable (>= 6.0.0)
listen (>= 3.0.0)
Expand Down Expand Up @@ -238,6 +242,8 @@ GEM
method_source (1.1.0)
mini_mime (1.1.5)
minitest (5.25.1)
mocha (2.4.5)
ruby2_keywords (>= 0.0.5)
msgpack (1.7.2)
multi_xml (0.7.1)
bigdecimal (~> 3.1)
Expand Down Expand Up @@ -357,6 +363,7 @@ GEM
ruby-lsp-rails (0.3.11)
ruby-lsp (>= 0.17.2, < 0.18.0)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
securerandom (0.3.1)
selenium-webdriver (4.24.0)
Expand Down Expand Up @@ -402,6 +409,8 @@ GEM
unicode-display_width (2.5.0)
uri (0.13.1)
useragent (0.16.10)
vcr (6.3.1)
base64
view_component (3.14.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
Expand All @@ -411,6 +420,10 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webmock (3.23.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.8.1)
websocket (1.2.11)
websocket-driver (0.7.6)
Expand Down Expand Up @@ -443,6 +456,7 @@ DEPENDENCIES
importmap-rails
jbuilder
lucide-rails!
mocha
pagy
pg (~> 1.5)
propshaft
Expand All @@ -460,7 +474,9 @@ DEPENDENCIES
tailwindcss-rails
turbo-rails
tzinfo-data
vcr
web-console
webmock

BUNDLED WITH
2.5.6
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ After cloning the repo, the basic setup commands are:
cd marketing
cp .env.example .env
bin/setup
bin/rails db:seed
bin/dev
```

Expand Down
31 changes: 0 additions & 31 deletions app/controllers/tickers_controller.rb

This file was deleted.

80 changes: 23 additions & 57 deletions app/controllers/tools_controller.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,34 @@
# This controller handles the logic for displaying and interacting with financial tools.
# It provides functionality for listing all tools and showing individual tool details.
class ToolsController < ApplicationController
# GET /tools
# Retrieves all tools from the database.
#
# @return [Array<Tool>] An array of all Tool objects.
def index
@tools = Tool.all
end

# GET /tools/:id
# Retrieves and displays a specific tool based on its slug.
# Also fetches additional data required for certain tools.
#
# @param id [String] The slug of the tool to be displayed.
# @return [Tool] The requested Tool object.
def show
@tool = Tool.find_by(slug: params[:id])

if @tool.slug == "home-affordability-calculator" || @tool.slug == "early-mortgage-payoff-calculator"
@loan_interest_rate_30 = fetch_mortgage_rate("MORTGAGE30US")
@loan_interest_rate_15 = fetch_mortgage_rate("MORTGAGE15US")
end

if @tool.slug == "stock-portfolio-backtest"
@stocks = Rails.cache.fetch("all_stocks", expires_in: 24.hours) do
Stock.select(:name, :symbol).map { |stock| { name: stock.name, value: stock.symbol } }
end
end

if @tool.needs_stock_data?
@stock_prices = StockPrice.fetch_stock_data
end
@tool = Tool.presenter_from tool_params.compact_blank
end

private

# Fetches the current mortgage rate for a given mortgage duration.
#
# @param mortgage_duration [String] The mortgage duration code (e.g., "MORTGAGE30US").
# @return [String, nil] The current mortgage rate as a string, or nil if the fetch fails.
def fetch_mortgage_rate(mortgage_duration)
cache_key = "mortgage_rate_#{mortgage_duration}"

Rails.cache.fetch(cache_key, expires_in: 24.hours) do
response = HTTParty.get("https://api.stlouisfed.org/fred/series/observations",
query: {
series_id: mortgage_duration,
api_key: ENV["FRED_API_KEY"],
file_type: "json",
limit: 1,
sort_order: "desc",
frequency: "w"
}
)

if response.success?
JSON.parse(response.body)["observations"].first["value"]
else
raise "Failed to fetch mortgage rate: #{response.code} #{response.message}"
end
def tool_params
params.permit :slug,
# Bogleheads Growth Calculator
:invested_amount, :stock_market_ticker, :international_stock_market_ticker, :bond_market_ticker, :stock_market_percentage, :international_stock_market_percentage, :bond_market_percentage,
# Compound Interest Calculator
:initial_investment, :monthly_contribution, :years_to_grow, :annual_interest_rate,
# Early Mortgage Payoff Calculator
:loan_amount, :original_term, :years_left, :interest_rate, :extra_payment, :savings_rate,
# Financial Freedom Calculator
:current_savings, :monthly_expenses, :annual_savings_growth_rate,
# Home Affordability Calculator
:loan_duration, :loan_interest_rate, :desired_home_price, :down_payment, :annual_pre_tax_income, :monthly_debt_payments, :hoa_plus_pmi,
# Inflation Calculator
:initial_amount, :years, :inflation_rate,
# Loan Calculator,
:loan_amount, :interest_rate, :loan_term, :loan_period, :date,
# Retirement Calculator
:annual_salary, :monthly_contribution, :annual_salary_increase, :current_age, :retirement_age, :annual_rate_of_return, :current_401k_balance, :employer_match, :salary_limit_match,
# ROI Calculator
:amount_invested, :amount_returned, :investment_period, :investment_length,
# Stock Portfolio Backtest
:benchmark_stock, :investment_amount, :start_date, :end_date, { stocks: [], stock_allocations: [] }
end
rescue StandardError => e
Rails.logger.error("Error fetching mortgage rate: #{e.message}")
nil
end
end
43 changes: 23 additions & 20 deletions app/helpers/application_form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,35 @@ def #{selector}(method, options = {})
end

def unit_field(method, options = {})
default_options = { class: "form-field__input pl-1" }
default_options[:class] = "form-field__input pl-0" if options[:label_right]
merged_options = default_options.merge(options)
default_options = { class: "form-field__input pl-1" }
default_options[:class] = "form-field__input pl-0" if options[:label_right]
merged_options = default_options.merge(options)

# Prepare options for the outer div
outer_div_options = { class: options.delete(:outer_div_class) }
# Prepare options for the outer div
outer_div_options = { class: options.delete(:outer_div_class) }

return super(method, merged_options) unless options[:label] || options[:label_right]
return super(method, merged_options) unless options[:label] || options[:label_right]

@template.form_field_tag(outer_div_options) do
label_html = if options[:label]
label_content = label(method, *label_args(options))
if options[:required] == false
label_content += @template.tag.span("Optional", class: "form-field__label")
end
@template.tag.div(class: "flex items-center justify-between") { label_content }
end
@template.form_field_tag(outer_div_options) do
label_html = if options[:label]
label_content = label(method, *label_args(options))

content = @template.tag.div(class: "flex items-center") do
@template.tag.span(options[:unit_symbol], class: "pl-3 pb-2 pt-1 text-sm text-gray-500") +
text_field(method, merged_options.except(:label)) +
(@template.tag.span(options[:label_right], class: "text-gray-500 text-xs mr-2") if options[:label_right]).to_s
if options[:required] == false
label_content += @template.tag.span("Optional", class: "form-field__label")
end

@template.tag.div(class: "flex items-center justify-between") { label_content }
end

content = @template.tag.div(class: "flex items-center") do
@template.tag.span(options[:unit_symbol], class: "pl-3 pb-2 pt-1 text-sm text-gray-500") +
text_field(method, merged_options.except(:label)) +
(@template.tag.span(options[:label_right], class: "text-gray-500 text-xs mr-2") if options[:label_right]).to_s
end

label_html.to_s + content
end
label_html.to_s + content
end
end

def submit(value = nil, options = {})
value, options = nil, value if value.is_a?(Hash)
Expand Down
12 changes: 0 additions & 12 deletions app/helpers/tools_helper.rb

This file was deleted.

Loading