ActiveModel::Validations on anonymous class
Asked Answered
P

2

17

I'm working on a small DataMapper-like ODM project, and I'm trying to make use of the ActiveModel::Validations component. However, I ran into a problem while writing tests - I'm using anonymous classes to construct my test schemas, however when it comes to running the validators, the ActiveModel::Name class is throwing an error: Class name cannot be blank. You need to supply a name argument when anonymous class given

Here's a simple code example to reproduce:

require 'active_model'

book_class = Class.new do
  include ActiveModel::Validations

  validates_presence_of :title

  def title; ""; end # This will fail validation
end

book_class.new.valid? # => throws error

The exception is only raised when there's a failed validator - I'm guessing the problem happens when it tries to construct the validation error message. So my question is:

  • I did a lot of searching, but couldn't find anyone trying to do something similar. Is this simply not possible with ActiveModel, or is there a workaround I'm not aware of?
Prognosis answered 21/1, 2013 at 2:29 Comment(0)
P
24

ActiveModel tries to get the model name (as you see here) when setting up the error messages. The quickest way to get around it (short of giving your anonymous class a name), is to give your class a class method model_name that returns an instance of ActiveModel::Name.

for example

require 'active_model'

book_class = Class.new do
  include ActiveModel::Validations
  def self.model_name
    ActiveModel::Name.new(self, nil, "temp")
  end
  validates_presence_of :title

  def title; ""; end # This will fail validation
end

book_class.new.valid? # => no error
Palaestra answered 21/1, 2013 at 2:57 Comment(3)
Thanks, exactly what I wanted! I noticed that ActiveModel::Name can take a class name as an argument, but didn't spot that I can return my own instance via self.model_name. Another reason not to program late at night... :PPrognosis
Why return an instance of ActiveModel::Name rather than a plain string from .model_name?Venavenable
This was 2 years ago, but IIRC, the validation process expects (expected?) an instance of ActiveModel::Name from that method.Palaestra
S
14

The error is being thrown in the initialize function of ActiveModel::Name here.

module ActiveModel
  class Name
    def initialize(klass, namespace = nil, name = nil)
      @name = name || klass.name

      raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?       
      # ...
    end
  end
end

So rather than defining a class method model_name that returns an ActiveModel::Name, you can define a class method name that returns a String.

require 'active_model'

book_class = Class.new do
  include ActiveModel::Validations
  validates_presence_of :title

  def self.name
    "Book"
  end

  def title; ""; end # This will fail validation
end

book_class.new.valid? # => false
Shive answered 2/10, 2014 at 19:39 Comment(1)
This is essentially because Class.new.name #=> nil, so either passing name to ActiveModel::Name or defining a name method on the class works.Grouper

© 2022 - 2024 — McMap. All rights reserved.