代码之家  ›  专栏  ›  技术社区  ›  jordan

Ruby on Rails-修改宏以传递计算值

  •  0
  • jordan  · 技术社区  · 2 年前

    我有一个用于缓存断言值的宏。我没有写这个问题,但想修改它,在块中使用动态值,而不是静态的最小值/最大值。

    # frozen_string_literal: true
    
    # Provides a lightweight model-level DSL for defining conditions with
    # Redis-backed configurable criteria.
    #
    # Example:
    #
    # class User
    #   include CriteriaCacheConcern
    #
    #   # Each criterion has a default value (second argument) that can be overridden by
    #   # a cached value. When evaluating criteria, we read first from cache (Redis) and
    #   # fall back on the default value if no cached value is present. Values are cached
    #   # under a key of this composition: 'model_class-condition-attribute'; e.g.,
    #   # 'user-auto_reject-application-score'
    #
    #   criteria_cache :auto_reject do
    #     range :application-score, [0, 20]
    #     range :user_age,          [0, 18]
    #   end
    # end
    #
    # # Set Redis-backed min/max
    # User.auto_reject_post_application.applicant_percentile_score.configure(min: 1, max: 21)
    #
    # # Are all criteria satisfiable for the user for this condition?
    # user.auto_reject_post_application?
    
    module CriteriaCacheConcern
      extend ActiveSupport::Concern
    
      included do
        def self.criteria_caches
          @criteria_caches || []
        end
    
        def self.criteria_cache(condition, &block)
          criteria_cache = CriteriaCache.new(condition, name).tap { |cc| cc.instance_eval(&block) }
    
          # Access the criteria cache object from the class
          #
          # Example:
          # User.auto_reject_post_application
          singleton_class.send(:define_method, condition) do
            criteria_cache
          end
    
          # Does the model meet the criteria?
          #
          # Example:
          # user.auto_reject_post_application?
          define_method("#{condition}?") do
            criteria_cache.meets_criteria?(self)
          end
    
          @criteria_caches ||= []
          @criteria_caches << criteria_cache
        end
      end
    
      class ConfigurableCriterion
        attr_reader :condition, :model_class, :attribute, :default_value
    
        def initialize(condition, model_class, attribute, default_value)
          @condition = condition
          @model_class = model_class
          @attribute = attribute
          @default_value = default_value
        end
    
        def value
          RedisHelper.cached_value(cache_key) || default_value
        end
    
        def configure(_params)
          raise NotImplementedError, 'ConfigurableCriterion subclass must implement #configure'
        end
    
        def meets_criterion?(_model)
          raise NotImplementedError, 'ConfigurableCriterion subclass must implement #meets_criterion?'
        end
    
        private
    
        def cache_key
          "#{model_class.underscore}-#{condition}-#{attribute}"
        end
      end
    
      # ConfigurableCriterion subclasses
      # --------------------------------
      #
      # All ConfigurableCriterion subclasses should implement the following interface:
      #
      # class ConfigurableCriterionSubclass < ConfigurableCriterion
      #   # Does the model satisfy the criterion?
      #   def meets_criterion?(model)
      #   end
      #
      #   # Update cached values
      #   def configure(params)
      #   end
      # end
    
      class Range < ConfigurableCriterion
        def min
          value.min
        end
    
        def max
          value.max
        end
    
        def meets_criterion?(model)
          (min..max).cover?(model.send(attribute))
        end
    
        def configure(params)
          min = params.fetch(:min) { default_value.min }.to_i
          max = params.fetch(:max) { default_value.max }.to_i
          raise ArgumentError, 'Maximum must be larger than minimum' if max <= min
    
          RedisHelper.cache_value(cache_key, [min, max])
        end
      end
    
      # ---------------------------------
    
      class CriteriaCache
        attr_reader :condition, :model_class, :criteria
    
        def initialize(condition, model_class)
          @condition = condition
          @model_class = model_class
          @criteria = []
        end
    
        # Define method for each configurable criterion type that adds
        # criterion object to @criteria collection
        #
        # Example of resultant method:
        # criteria_cache.range(attribute, default_value)
        ConfigurableCriterion.descendants.each do |klass|
          define_method(klass.name.demodulize.underscore) do |attribute, default_value|
            criterion = klass.new(condition, model_class, attribute, default_value)
    
            @criteria << criterion
    
            # Return configurable criterion object
            #
            # Example:
            # User.auto_reject_post_application.applicant_percentile_score
            define_singleton_method(criterion.attribute) do
              criterion
            end
          end
        end
    
        def meets_criteria?(model)
          @criteria.all? { |c| c.meets_criterion?(model) }
        end
      end
    end
    

    并按原样使用

    criteria_cache :auto_assign_user do
      range :user_score, [80 , 100]
    end
    

    我想更改宏,以便能够根据计算值设置范围。例如 User 模型有一种方法 lowest_score_to_auto_assign 这将计算出在 auto_assign_user 块。

    我还不够熟悉,不知道该如何将其传递给 CriteriaCacheConcern

    0 回复  |  直到 2 年前