1

I want to do a compact error checking assignment in ruby.

class User
  attr_accessor :x
end

user = User.new
user.x = 5
a = b || user.x

I want to figure out which of these is the first valid attribute and assign it, similarly to how javascript handles different API's, i.e.:

var AudioContext = window.AudioContext||window.webkitAudioContext;
audioContext = new AudioContext();

and figure out which was valid.

With ruby however, similar syntax gives errors when I reference an undefined variable. i.e.:

a = 10
b = 7
c = a || b
c # => 10

vs

a = 10
c = b || a # => Error: b is undefined

Is there a clean way to do this? Or at the very least, what is the best way to do this?

I'm working with a large code that I haven't created, and I am not permitted to change it.

UPDATE: I think the real use case is kind of relevant to this question so i'll explain it.

I have a module which saves something to the DB every time a model in rails is updated, this update requires an id field, this id field is inside the model that includes my module, however not every model maintains the same naming convention for this id. The ternary operator equivalent of what I want to do is

id = defined?(self.id) ? self.id : defined?(self.game_id) ? self.game_id : defined?(self.app_id) ? self.app_id : nil

which is hard to read and write compared to the js equivalent

3
  • 4
    yeah, ruby is not like javascript in this regard (treating undefined vars as falsy). What is the real use-case, though? I doubt it's the case of undefined local variable (in which case, you can just initialize it to nil beforehand) Commented Mar 29, 2016 at 10:10
  • Your right that is ambiguous, its just properties of objects i'm checking not local variables, I got lazy with my sample code Commented Mar 29, 2016 at 11:25
  • Prepare better sample code then :) Commented Mar 29, 2016 at 11:39

2 Answers 2

3

You can use defined? to test if a name refers to something recognizable in current scope (method, local variable, etc):

c = defined?(b) ? b : a # of course this assumes that 'a' is defined

Although it's pretty iffy that you're assigning from local variables that haven't been defined - how does that come up?

Or are you always testing for properties? In which case, respond_do? may be the better choice.

Edit

I was using the ternary operator as an example, you could always use an if/elsif/else block, of course.

Since you're only testing methods, respond_to? is more convenient than defined? because it takes symbols, rather than expressions, to which you can apply any logic you want:

def invoke_first *names
  names.each do |name|
    if respond_to? name
      return send name
    end
  end
  return nil # or, more likely, raise something
end

or, more concisely:

def invoke_first *names
  send names.find(lambda {raise 'no method'}){|n| respond_to?(n)}
end

include in your model and use as:

invoke_first(:foo_id, :bar_id, :baz_id)
Sign up to request clarification or add additional context in comments.

1 Comment

Yes I am always checking object properties but its a little more complex than a single ternary operator, the real use case is a module which is included and can be used to update a database based on an id, however depending on where its included the objects have different names for the id, i.e. the ternary operator solution would be something like "id = defined?(self.id) ? self.id : defined?(self.game_id) ? self.game_id : defined?(self.app_id) ? self.app_id : nil" which is obviously stupidly hard to read
0

Okay so there is a much more concise way of doing this, but it has a side-effect of assigning something to the undefined var.

This breaks

a = 1
c = b || a # => b is undefined

This works

a = 1
c = b ||= a # => c == 1, b == 1

The above assigns b to c if b is valid, then falls back on a. a is only assigned to b (and c) if b is undefined/invalid.

Comments

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.