0

I am running into an issue, where I need to check if a class exists. However, I am passing the class to a variable and trying to check it from there.

My issue is I need to pass the actual constant for defined?() to work, but I'm passing a variable, so instead of seeing a constant, it sees a method or variable.

obj is a rails model instance, for example, a specific User, or a specific Car.

  def present(obj, presenter_class=nil, view_context=nil)
    klass = presenter_class || "#{obj.class}Presenter".constantize
    if defined?(klass) == 'constant' && klass.class == Class 
      klass.new(obj, view_context)
    else
      warn("#{self}: #{klass} is not a defined class, no presenter used")
      obj
    end
  end

Pry Output:

[1] pry(ApplicationPresenter)> defined?(klass) 
=> "local-variable"

I tried the below, but I get a method back...

[18] pry(ApplicationPresenter)> defined?("UserPresenter".constantize)
=> "method"

How can I fix this issue?

8
  • 2
    How about klass.is_a?(Class)? Commented Dec 19, 2019 at 17:44
  • @CarySwoveland - I'm sure this is an idiotic question (sorry), but isn't everything a Class? After all this time, I find I'm still just learning ruby. Commented Dec 19, 2019 at 17:47
  • No, can be methods or as the above shows variables,etc Commented Dec 19, 2019 at 18:02
  • Is it really necessary that you handle this else case to show the warning and continue the program? Why don't you want it to throw an error in that case? Commented Dec 19, 2019 at 18:08
  • All Ruby objects are instances (members) of a class. Variables, however, are not objects. If the argument of Object#defined? holds what may be the name of a local variable, the method will return "local-variable" if a local variable with that name exists; else it will return nil. To my knowledge this is the only situation where a reference to a local variable concerns the variable itself as opposed to the object it holds. Commented Dec 19, 2019 at 19:36

1 Answer 1

1

Well, apparently Object#defined? does not the thing that you hoped it would do.

tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.

Your goal looks like you are rebuilding what the draper gem is doing with .decorate... Don't forget that most of the gems are open source and you can use that for trying things on your own. See for example the decorator_class method from them

decorator_name = "#{prefix}Decorator"
decorator_name_constant = decorator_name.safe_constantize
return decorator_name_constant unless decorator_name_constant.nil?

They use the method safe_constantize and this apparently returns nil when the constant is not available.

2.6.5 :007 > class UserPresenter; end;
 => nil 
2.6.5 :008 > 'UserPresenter'.safe_constantize
 => UserPresenter 
2.6.5 :009 > 'ForgottenPresenter'.safe_constantize
 => nil 

To me that looks exactly like what you need, and it also safer than using constantize

  def present(obj, presenter_class=nil, view_context=nil)
    klass = presenter_class || "#{obj.class}Presenter".safe_constantize
    if klass != nil
      klass.new(obj, view_context)
    else
      warn("#{self}: #{klass} is not a defined class, no presenter used")
      obj
    end
  end
Sign up to request clarification or add additional context in comments.

2 Comments

It is more common and more economical to reference a method's document by writing, for example, [Object#defined?](URL here), which appears as Object#defined?.
Can you explain this more? What do you mean [Object#defined?](url here), Can you post an example specific to this issue?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.