Detect,> =,<= Ruby中的运算符,并在.where方法中使用(无活动记录)

问题描述 投票:1回答:2

在普通红宝石中(无活动记录)

def where(options)
end

选项可以是键,值对,也可以是>, =,<=

之类的比较

如果我有一组记录...

record = Struct.new(:name, :address, :amount) 

我如何测试哪些内容并对其进行过滤?

例如,如果我这样做:

.where(:amount > 5)

vs

.where(name: 'John')

甚至

.where(name: 'John', :amount>5)

我希望它退还符合要求的物品。

array_of_records.select { |record| record.name == 'John'  } 
array_of_records.select { |record| record.amount > 5  } 

我以为我需要这样的东西,但不确定从哪里开始。

option_type(option)
   case option
   when keyvalue?(option)
   when gtr_then?(option)
   when gtr_then_eql?(option)
   when less_then?(option)
   when less_then_eql?(option)
   end
end


keyvalue?(mykey: 5)
   # Code detects key value pair
   # Return true
end

gtr_then?(mykey > 5)
   # Code detects > operator
   # Return True
end

less_then?(mykey < 5)
   # Code detects < operator
   # Return true
end

less_then_eql?(mykey <= 5)
   # Code detects <= operator
   # return true
end

gtr_then_eql?(mykey >= 5)
   # Code detects >= operator
   # return true
end
ruby activemodel
2个回答
2
投票

嗯,甚至ActiveRecord都使用字符串表示形式来实现这种选择:.where('amount > 5')AR这样做可能会更容易,因为它可以直接传递到数据库。对于您的情况,您将必须解析字符串,找到运算符和操作数,然后执行操作。

但是还有一种选择(也受AR支持)来使用范围。在Ruby 2.6中具有无限范围是很容易的:.where(amount: 5..)在以前的版本中,您可以使用.where(amount: 5..Float::INFINITY)

然后,您只需要检查参数是否是范围,以及它是否覆盖值(请确保使用cover?而不是include?,因为您不想在无限数组上进行迭代)


2
投票

混合@ Richard-Degenne的评论和Maxim的答案,您可以编写这样的内容。

Symbol定义:>,:Refinements。使用后果自负!

class Record
  attr_reader :name, :address, :amount
  def initialize(name, address, amount)
    @name = name
    @address = address
    @amount = amount
  end

  def to_s
    [name, address, amount].join(' ')
  end

  def inspect
    to_s
  end
end

module Enumerable
  def where(query)
    select do |record|
      case query
      when Hash
        query.all? do |key, pattern|
          pattern === record.send(key)
        end
      when WhereComparator
        query.match? record
      end
    end
  end
end

class WhereComparator
  def initialize(sym, operator, other)
    @sym = sym
    @operator = operator
    @other = other
  end

  def match?(record)
    record.send(@sym).send(@operator, @other)
  end
end

module MyWhereSyntax
  refine Symbol do
    [:<, :<=, :==, :>=, :>].each do |operator|
      define_method operator do |other|
        WhereComparator.new(self, operator, other)
      end
    end
  end
end

using MyWhereSyntax

records = [
  Record.new('John', 'a', 7),
  Record.new('Jack', 'b', 12),
  Record.new('Alice', 'c', 19),
  Record.new('John', 'd', 2),
]

p records.where(name: 'John')
#=> [John a 7, John d 2]
p records.where(name: 'John', amount: 2)
#=> [John d 2]
p records.where(name: 'John').where(:amount > 5)
#=> [John a 7]
p records.where(name: 'John').where(:amount > 7)
#=> []
p records.where(:amount > 8).where(:address <= 'c')
#=> [Jack b 12, Alice c 19]
p records.where(name: /^J...$/)
#=> [John a 7, Jack b 12, John d 2]

作为奖励,您可以写:

long_enough = :size > 7
# => #<WhereComparator:0x00000000017072f8 @operator=:>, @other=7, @sym=:size>
long_enough.match? 'abcdefgh'
# => true
long_enough.match? 'abc':
# => false
© www.soinside.com 2019 - 2024. All rights reserved.