Project

General

Profile

Feature #14059

Refine multiple classes in one call

Added by kddeisz (Kevin Deisz) over 1 year ago. Updated 12 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:83582]

Description

Consider the following problem - I would like to be able to validate that a JSON string contains all things that can represent positive integers. In this case, it works well with refinements, because I can:

class PositiveJSONValidator < ActiveModel::EachValidator
  using Module.new {
    refine String do
      def positive_integer?
        match?(/[1-9](?:[0-9])*/)
      end
    end

    refine Integer do
      def positive_integer?
        self > 0
      end
    end

    refine Float do
      def positive_integer?
        self == self.to_i
      end
    end

    refine NilClass do
      def positive_integer?
        false
      end
    end

    refine Array do
      def positive_integer?
        false
      end
    end

    refine Hash do
      def positive_integer?
        false
      end
    end
  }

  def validate_each(record, attribute, value)
    return if valid_positive_json?(value)
    record.errors[attribute] << options[:message] || 'is invalid'
  end

  private

  def valid_positive_json?(value)
    JSON.parse(value).all? do |key, value|
      key.positive_integer? && value.positive_integer?
    end
  rescue JSON::ParserError
    false
  end
end

However, this is kind of annoying because I need to specify each class manually. What I'd love to be able to do is group classes by passing all of the ones that need to be refined in the same way, as in:

refine NilClass, Array, Hash do
  def positive_integer?
    false
  end
end

Is this something that people would consider? It seems like a good use case for refinements because I just want to send a message to an object, so basically I want a default case. I could just refine Object but that feels wrong, I'd rather get an undefined method exception so that I know something unexpected occurred. If accepted I'd be happy to submit a patch.

History

Updated by rrroybbbean (RRRoy BBBean) over 1 year ago

On Thu, 2017-10-26 at 17:30 +0000, kevin.deisz@gmail.com wrote:

refine NilClass, Array, Hash do
  def positive_integer?
    false
  end
end

Is this something that people would consider? It seems like a good
use case for refinements because I just want to send a message to an
object, so basically I want a default case. I could just refine
Object but that feels wrong, I'd rather get an undefined method
exception so that I know something unexpected occurred. If accepted
I'd be happy to submit a patch.

Yes. This is awesome and excellent. For curiosity, why not enclose the
classes in []?

Updated by duerst (Martin Dürst) over 1 year ago

I'm really not sure about this. If we need this for refinements, why don't we have for classes in the first place? E.g.:

class Foo, Bar, Baz
  def positive_integer?
    false
  end
end

It seems strange to introduce syntactic sugar just for refinements.

Updated by shevegen (Robert A. Heiler) over 1 year ago

Perhaps it could be an Array input for Module#refinements?

There he could perhaps specify the classes that are to be
modified. So the above example would then be:

refine [NilClass, Array, Hash] do
  def positive_integer?
    false
  end
end

instead.

http://ruby-doc.org/core-2.4.2/Module.html#method-i-refine

But I know too little about refinements and also too little
about what Kevin wants to achieve, aside from him using
less syntax for batch-modification, which I think is
understandable. :)

Updated by jeremyevans0 (Jeremy Evans) over 1 year ago

I think the natural way to handle this is a loop:

[NilClass, Array, Hash].each do |c|
  refine c do
    def positive_integer?
      false
    end
  end
end

Because of that I don't think we need new syntax to support this.

Updated by nobu (Nobuyoshi Nakada) over 1 year ago

  • Description updated (diff)

And also a Module.

using Module.new {
  m = Module.new {
    def positive_integer?
      false
    end
  }
  [NilClass, Array, Hash].each {|k| refine(k) {include m}}
}

Updated by kddeisz (Kevin Deisz) 12 months ago

I agree with the points above, so this issue can be closed (or someone can teach me how to close these, I'm not entirely sure how).

Also available in: Atom PDF