3

I want to create an object that can be initialized in two different ways. I found 3 different way's of accomplishing the same thing, I want to know which of my methods is the best one, and if there is another better way of doing it.

Method

attr_accessor :id, :status, :dateTime
  def initialize *args
    if args.length == 1
      puts args[0]
    @id = args[0]['id']
    @status = args[0]['status']
    @dateTime = args[0]['dateTime']
    else
      @id = args[0]
      @status = args[1]
      @dateTime = args[2]
    end
  end

Method 2: (note that I need to set the parameters by hand on this one as second way)

  attr_accessor :id, :status, :dateTime
  def initialize hash = nil
    if hash != nil
    @id = hash['id']
      @status = hash['status']
      @dateTime = hash['dateTime']
    end
  end

Method 3: (note that I need to set the parameters by hand on this one as second way AND that it is almost the same as my second way, just not in the constructor)

attr_accessor :id, :status, :dateTime
  def initialize
  end

  def self.create hash
    if hash != nil
      obj = Obj3.new
      obj.id = hash['id']
      obj.status = hash['status']
      obj.dateTime = hash['dateTime']
      return obj
    end
  end

Thanks in advance!

Faling Dutchman
  • 211
  • 1
  • 4
  • 17
  • What arguments do they take? – sawa Mar 23 '16 at 11:37
  • `Method 2: (note that I need to set the parameters by hand on this one as second way)` Isn't it evident that the second method/way follows the second way? – sawa Mar 23 '16 at 11:39
  • @sawa Maybe I needed to be more clear. It takes a hash with id, status and dateTime OR I could initialize an object without parameters and say object.id = "an id" etc. – Faling Dutchman Mar 23 '16 at 12:08
  • Have a look at [Keyword arguments](https://robots.thoughtbot.com/ruby-2-keyword-arguments) – Wand Maker Mar 23 '16 at 14:46

1 Answers1

5

I would try using a hash for your constructor like the code below adapted from DRY Ruby Initialization with Hash Argument

class Example
  attr_accessor :id, :status, :dateTime

  def initialize args
    args.each do |k,v|
      instance_variable_set("@#{k}", v) unless v.nil?
    end
  end
end

That way setting each of your properties in the constructor becomes optional. As the instance_variable_set method will set each property if the has contains a value for it.

Which means you could support any number of ways to construct your object. The only downside is you might have to do more nil checking in your code but without more information it is hard to know.

Creating a new Object - Usage Examples

To create a new object with this technique all you need to do is pass in a hash to your initialiser:

my_new_example = Example.new :id => 1, :status => 'live'
#=> #<Example: @id=1, @status='live'>

And its flexible enough to create multiple objects without certain properties with one constructor:

my_second_new_example = Example.new :id => 1
#=> #<Example: @id=1>

my_third_new_example = Example.new :status => 'nonlive', :dateTime => DateTime.new(2001,2,3)
#=> #<Example: @id=1, @dateTime=2001-02-03T00:00:00+00:00>

You can still update your properties once the objects have been created:

my_new_example.id = 24
Community
  • 1
  • 1
Tom
  • 480
  • 4
  • 13
  • But now I need to be able to set the values via hash or via interaction with the object, like initializing an object as obj = Example.new; obj.id = "an id" – Faling Dutchman Mar 23 '16 at 12:11
  • 1
    Not quite - I will update the answer with some usage examples. – Tom Mar 23 '16 at 12:43
  • So if I understand correctly, I can initialize the object with any amount of parameters, the ones that are not given will be left nil, and I can set them at a later time? Also if I understand it correctly I can say my_new_example = Example.new my_new_example.id = 23 – Faling Dutchman Mar 23 '16 at 13:06
  • 1
    For your first point. Yes as long as your define your properties (using attr_accessor in the examples so far) then you initialise your object with a hash. The key is the name of your param and the value the value to set it to. If your hash does not contain a key / value for a property that property will be nil. Your second point. Yes you can do that as long as the my_new_example.id = 23 is on a new line! – Tom Mar 23 '16 at 13:14
  • @FalingDutchman you could also just use the [`OpenStruct`](http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html) Class which will allow you to initialize with values but also set arbitrary ones after the fact. That being said if you want to use this i would recommend adding `attr_accessor` to the initialize block e.g. `self.class_eval { attr_accessor k}` inside the args loop this will create getter/setter methods for each of those attributes. – engineersmnky Mar 23 '16 at 14:54
  • @engineersmnky If I do that, and want to make a JSON out of it, I would get '@'id, '@'status, '@'dateTime, with no real solution to remove the '@'s, so I wrote my own to_json for my custom classes, that is the best way for me, AND I need to have the classes available as classes because I need to be able to set information. On the solution I picked I added an if args != nil, to make the class initializable without parameters. – Faling Dutchman Mar 23 '16 at 14:59
  • @FalingDutchman the `JSON` module adds functionality to the `OpenStruct` for the purpose of calling `to_json`. Not sure what your overall issue is here but you could retain the set instance variables in a class level or singleton level array and then use those to generate JSON as well. Since your post said nothing about JSON obviously if you have questions that would need to be a new post. – engineersmnky Mar 23 '16 at 17:48