Skip to content

Instantly share code, notes, and snippets.

@rob-mcgrail
Last active September 6, 2017 08:03
Show Gist options
  • Save rob-mcgrail/48c20d4973e48b65562a5af1ebf03760 to your computer and use it in GitHub Desktop.
Save rob-mcgrail/48c20d4973e48b65562a5af1ebf03760 to your computer and use it in GitHub Desktop.
module Users
class Profile < ApplicationRecord
include ::Calculator::Profile # Let anything with this act as a profile?
end
end
module Trends
class Profile < ApplicationRecord
include ::Calculator::Profile # And if we need ad-hoc profiles,
# make classes for them
end
end
module Retailers
class Plan < ApplicationRecord
include ::Calculator::Plan # Let anything with this act as a plan, suspect
# there's calculation stuff around plans that
# could be rolled into the plan via this module.
end
end
module Calculator
module Profile
def estimated_usage
# the logic for surfacing usage in here, memoized as necessary.
end
def other_profile_methods
# ...
end
# Convenience arbitrary calculations
def calculate_for(plan:, calculator: nil, formatter: nil)
calculator ||= ::Calculator::Calculator
formatter ||= ::Calculator::ResultsFormatter
result = calculator.call(self, plan).result
formatter.call(result).result
end
end
end
module Calculator
module Plan
def useful_calculation_specific_plan_info_methods
# ...
end
# Convenience arbitrary calculations
def calculate_for(profile:, calculator: nil, formatter: nil)
calculator ||= ::Calculator::Calculator
formatter ||= ::Calculator::ResultsFormatter
result = calculator.call(profile, self).result
formatter.call(result).result
end
end
end
module Calculator
class CalculateService
prepend SimpleCommand
attr_accessor :profile, :plans, :calculator, :formatter
def initialize(plans:, profile:, calculator: nil, formatter: nil)
@plans = plans # we'll raise in calculator if this isn't an enumerable of plans
@profile = profile # we'll raise in calculator if this doesn't have calc::profile mixed in
@calculator = calculator || ::Calculator::Calculator
@formatter = formatter || ::Calculator::ResultsFormatter
end
def call
results = plans.map do |plan|
calculator.call(profile, plan).result
end
formatter.call(results).result
end
end
end
module Calculator
class ProfilePlanSet
prepend SimpleCommand
attr_accessor :profile
def initialize(profile)
@profile = profile
end
def call
::Retailers::PublishedPlan.includes(:retailer).search_for_results(
network_location_id: location.id,
plan_type_id: profile.electricity_plan_type_id,
estimated_annual_usage: profile.estimated_usage
)
end
end
end
command = ::Calculator::CalculateService.call(
profile: ::Users::Profile.find_by_hashid(params[:id]),
plans: ::Calculator::ProfilePlanSet.call(profile).result
)
@jamesmccann-zz
Copy link

@roboMC I think it'd be cool to get the search and filter_by_usage methods out of PublishedPlan as they're specific to calculations, would turning the ProfilePlanSet above into something like this work?

module Calculator
  class AvailablePlans
     attr_reader :plans, ...

     def initialize(network_location_id, plan_type_id, estimated_usage, plans: Retailers::PublishedPlan.all)
       # ... store instance vars
       @plans = plans.extending(Scopes)
     end
     
     def call
       plans.includes(:retailer)
         .by_network_location(network_location_id)
         .by_plan_type(plan_type_id)
         .open # exclude closed plans
         .unarchived
         .filter_by_usage(estimated_usage)
     end

     module Scopes
       def filter_by_usage(estimated_annual_usage, threshold: 500)
        #rewrite this to use SQL rather than array ops
        plans
          .delete_if { |p| p.min_annual_usage.present? && p.min_annual_usage != 0 && (estimated_annual_usage + threshold) < p.min_annual_usage }
          .delete_if { |p| p.max_annual_usage.present? && p.max_annual_usage != 0 && (estimated_annual_usage - threshold) > p.max_annual_usage }
      end
    end

  end
end


       
     
  

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