-2
\$\begingroup\$

Let's say that I have the following methods

def name(value)
    if value
        attribute['name'] = value
    end

    return attribute['name']
end

def surname(value)
    if value
        attribute['surname'] = value
    end

    return attribute['surname']
end

def age(value)
    if value
        attribute['age'] = value
    end

    return attribute['age']
end

def gender(value)
    if value
        attribute['gender'] = value
    end

    return attribute['gender']
end

As you see the structure is incredible similar. Im just defining getters and setters.

How could this be done in a more DRY manner?

\$\endgroup\$
  • \$\begingroup\$ Yeah, I know I could create a different signature for the methods. But I would like to be able to implement the methods in this way as well. \$\endgroup\$ – Enrique Moreno Tent Nov 25 '16 at 11:27
  • \$\begingroup\$ What is attribute, where is initialized? why is it singular? Please, show more code \$\endgroup\$ – tokland Nov 26 '16 at 15:04
  • \$\begingroup\$ It is just some fake code I created. The point I am trying to make is how to create methods dynamically, that look very alike. \$\endgroup\$ – Enrique Moreno Tent Nov 26 '16 at 17:24
  • 2
    \$\begingroup\$ Fix indentation (2 spaces), use attribute accessors, don't use explicit returns on last line of method. If you are looking for help on metaprogramming, why not try StackOverflow? You are not asking for a review, you are asking for a solution. Go to SO. \$\endgroup\$ – Damien Roche Nov 28 '16 at 17:14
  • \$\begingroup\$ getters and setters are normally used for attributes of a class, these are already as DRY as it gets. there is nothing to be done better with these. \$\endgroup\$ – Malachi Nov 28 '16 at 18:42
1
\$\begingroup\$

In order to define accessors dynamically, I would use methods like #attribute_name for the getter and #attribute_name=(value) to be able to set values to nil or false (which your code prevents). Plus your API won't be confusing to other rubyists (typing foo_instance.name(nil) in order to get a value isn't really the Ruby Way).

class Foo
  attr_accessor :foo, :bar, :baz
end

Now, if you need to do something else entirely and define multiple methods with the same body, I would recommend using Class#define_method

class Foo
  def initialize(attributes = {})
    @attributes = attributes
  end

  %w(foo bar baz).each do |name|
    define_method name do
      @attributes[name] # consider using Hash#fetch here
    end

    define_method "#{name}=" do |value|
      @attributes[name] = value
    end
  end
end

For the sake of completeness, here's my answer without changing anything to the original code behavior :

%w(name surname age gender).each do |attribute_name|
  define_method attribute_name do |value|
    attribute[attribute_name] = value if value
    attribute[attribute_name]
  end
end
\$\endgroup\$
0
\$\begingroup\$
class Foo
  [:foo, :bar, :baz].each do |method_name|
    defin_method(method_name) do |value|
      if value
        attribute[method_name.to_s] = value
      end

     return attribute[method_name.to_s]
    end
  end
end

This is one way to define dynamic fields. Check that the array [:foo, :bar, :baz] can comes from some complex function, not just an static array.

Another way is using method_missing

class Foo
  def get_methods
    # some complex code here that returns an array like [:foo, :bar, :baz]
  end

  def method_missing(method, *args, &block)
    if get_methods.include? method
      # your code here
    else
      super
    end
  end
end

The second way is more dynamic, because can changes after initialize the class. The first example only changes at beginning, but the second is on the fly.

Check this.

\$\endgroup\$
-2
\$\begingroup\$

We can write function as follows

def attribute(key, value=nil)
  value && (attribute[key] = value)
end

Using above method you can make structure DRY and dynamic. You can avoid if condition by using guard clause &&

\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and how it improves upon the original) so that the author can learn from your thought process. \$\endgroup\$ – jacwah Nov 25 '16 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.