0

I have a function that is inserting a record into my DB (MySQL). It has many columns, many of which have default values in the DB. Passing in values for these variables is therefore optional.

def assign_X_to_Y( options = {} )
 . . . 
 @bar.var1 = options[:foo]
 . . .
end

I would like to do the following:

-If a variable exists (ex: options[:foo]), add it to the record I'm making.

@bar.var1 = options[:foo]

-If it doesn't, I don't want to add it--I want to use the DB default.

I know I can simply do an if:

if options[:foo]
   @bar.var1 = options[:foo]
end

But I have a lot of these variables and so I think there must be a nicer way that having loads of if-statements. Something like the "if doesn't exist set to null" expression:

@bar.var1 = options[:foo] || nil

Is there anything like what I am saying? I can't use the above expression because I don't want to set it to null (which I think it would do), I want to use the default value…

Thanks in advance!

3
  • 1
    Are you assigning the values to a model or creating a SQL statement? Please add the assign_X_to_Y method to your question. Its impossible to give refactoring advice about an method which is not even shared with the reader. Commented Jun 23, 2015 at 21:29
  • Added @bar.var1 = options[:foo] to the method…does that clarify that I am adding values to a model? Commented Jun 24, 2015 at 3:28
  • 1
    Yeah, if its a model just do @bar.assign_attributes(options) Commented Jun 24, 2015 at 4:07

2 Answers 2

1

If @bar is an model you can simply pass a hash:

Bar.create(hash) # creates a Bar with the defaults from your schema
@bar.assign_attributes(hash)
@bar.update(hash) # same as object but commits the changes to the db

If bar is a Plain Old Ruby class you can give it the same functionality by:

class Bar

  attr_accessor :foo
  attr_accessor :baz
  attr_accessor :woggle

  def initialize(hash)
     assign_values(hash)
  end

  def assign_attributes(hash)
    assign_values(hash)
  end 

  private 

  def assign_values(hash)
    hash.each do |k,v|
      send "#{k}=", v
    end
  end
end

Then I can simply create an object with:

Bar.new(foo: 1, baz: 3)

Note that this will respect object encapsulation - if I try to do:

Bar.new(haxxored: true)

It will raise a NoMethodError. Just like @bar.haxxored = true.

Sign up to request clarification or add additional context in comments.

Comments

0

If I'm understanding your question correctly, the best way to handle this would be to use the public_send method in Ruby:

def set_new_property(obj, prop_name, prop_value)
  obj.class.__send__(attr_accessor: "#{prop_name}")
  obj.public_send("#{prop_name}=", prop_value)
end

Bear in mind that you'll have to set explicit attribute accessors for each potential property being assigned.

1 Comment

Or you could just do instance_variable_set if you are going to trump encapsulation anyways.

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.