0

I'm refactoring my code and I want to have some logic passed to a parameter when creating an instance of a class. To best describe the problem, I will share some code.

I have this method that takes in an object as an argument.

def common_attributes(klass)
  klass.new(id: some_id, name: some_name, role_id: some_role.id)
end

I want to be able to use this to do something like:

def common_attributes(klass)
  klass.new(id: some_id,
            name: some_name
            role_id: some_role.id unless Module::class)
end

This is because I have several classes inside my module. Two of these classes take the same three arguments i.e id and name, role_id. The other class takes two arguments id and name. Please note these are example values and the implementation may not make sense problem I but what is important is to understand the problem I have.

This is how I would use the function:

common_attributes(MyModule::Myclass)

common_attributes(MyModule::DifferentClass)

common_attributes(MyModule::AnotherDifferentClass)

How will I ensure that the other class that takes fewer arguments receives the required parameters and leaves out what it does not need?

NB I can not edit the classes to include optional parameters.

kevinsamoei
  • 73
  • 2
  • 8
  • Is `company` an instance of some class? How is it visible within `common_attributes`? Also, `Module::class => Class`, which is always truthy. – Cary Swoveland Jan 11 '19 at 23:58
  • `company` is an instance of a class `Company` – kevinsamoei Jan 12 '19 at 00:03
  • Try this: `class Company; end; company = Company.new; def meth; company; end; meth #=> NameError (undefined local variable or method 'company' for main:Object)`. – Cary Swoveland Jan 12 '19 at 00:08
  • @CarySwoveland Don't put much thought on `company,` the values could be coming from anywhere, could even be hardcoded. Let me edit it to show this. – kevinsamoei Jan 12 '19 at 00:19
  • Before your edit you only had to make `company` the method's second argument to deal with my first objection. Now you need to make `some_idea`, `some_name` and `some_role` method arguments. Moreover, as I said before, `Module::class` returns `Class`, so `role_id: some_role.id unless Module::class` is the same as `role_id: some_role.id unless Class`, which, because `Class` is truthy, is the same as `role_id: some_role.id`. It's hard to generate interest among readers to answer your question when the code contains such fundamental mistakes. – Cary Swoveland Jan 12 '19 at 03:42

2 Answers2

1

Maybe arity is what you are looking for

def common_attributes(klass)
  args = { id: some_id, name: some_name }
  args[:role_id] = some_role.id if klass.instance_method(:initialize).arity == 3
  klass.new args                
end
periswon
  • 596
  • 5
  • 9
1

You should test for the class of klass inside your method. If, for example, the class that only accepts two attributes is MyModule::Myclass, and everything else accepts tree attributes, then either of these solutions should work:

def common_attributes(klass)
  case klass
  when MyModule::Myclass
    klass.new(id: some_id, name: some_name)
  else
    klass.new(id: some_id, name: some_name, role_id: some_role.id)
  end
end

or:

def common_attributes(klass)
  options = { id: some_id, name: some_name }

  if klass.is_a?(MyModule::Myclass)
    options[:role_id] = some_role.id
  end

  klass.new(**options)
end

The checks can grow to cover more special cases as appropriate.

tompave
  • 11,952
  • 7
  • 37
  • 63
  • Are `some_id` and `some_name` unseen methods? Presumably, `some_role` is a local variable that holds an instance of a class, but it is not initialised. You should not be repeating the OP's errors. – Cary Swoveland Jan 12 '19 at 03:45
  • This is exactly what I was looking for. Thank you – kevinsamoei Jan 12 '19 at 08:38
  • @CarySwoveland `some_id`, `some_name` and `some_role` come from the OP's snippet and, yes, they look like methods available in that scope. I wouldn't consider them "errors" though, just something to simplify the example. – tompave Jan 13 '19 at 15:25