class Device
def initialize(device_id, data_resource)
@id = device_id
@data_resource = data_resource
end
def display_device
mode = @data_resource.get_display_device_mode(@id)
presets = @data_resource.get_display_device_presets(@id)
summary = "display_device: #{mode} ($#{presets})"
return "* #{summary}" if presets == "XTC909"
summary
end
def chip
mode = @data_resource.get_chip_mode(@id)
presets = @data_resource.get_chip_presets(@id)
summary = "chip: #{mode} ($#{presets})"
return "* #{summary}" if presets == "XTC909"
summary
end
def input_device
mode = @data_resource.get_input_device_mode(@id)
presets = @data_resource.get_input_device_presets(@id)
summary = "input_device: #{mode} ($#{presets})"
return "* #{summary}" if presets == "XTC909"
summary
end
end
As you can see from the code above, there is quite a bit of redundancy within the methods. Regardless of whether metaprogramming is the best way to reduce this redundancy, I am hoping to learn how to use metaprogramming in Ruby to reduce some of the repetitiveness here if someone could provide some suggestions.
Here's a version that uses metaprogramming, though I'd also remove the duplication by putting it in a method where it belongs.
class Device
def initialize(device_id, data_resource)
@id = device_id
@data_resource = data_resource
end
def resource_summary(resource_name)
mode = @data_resource.send("get_#{resource_name}_mode", @id)
presets = @data_resource.send("get_#{resource_name}_presets", @id)
summary = "#{resource_name}: #{mode} ($#{presets})"
return "* #{summary}" if presets == "XTC909"
summary
end
def self.resource_accessor(*names)
names.each {|resource| define_method(resource) {resource_summary resource}}
end
resource_accessor :display_device, :chip, :input_device
end
If you really didn't want to make a method for that functionality, you could just replace the resource_summary
method call with the body of the resource_summary
method.
Something like this could work so you can define 'components' (or whatever they are) declaratively. This is overkill for this sort of example, but you can use it when you need to define dozens/hundreds of these things, or you're putting it as part of some framework (like rails does).
The component
class level method would usually live in some other module that gets included into the class rather than declaring it inline where it's used like this.
class Device
class << self
def component(component_name)
define_method(component_name) do
mode = @data_resource.send("get_#{component_name}_mode", @id)
presets = @data_resource.send("get_#{component_name}_presets", @id)
summary = "#{component_name} : #{mode} ($#{presets})"
presets == "XTC909" ? "* #{summary}" : summary
end
end
end
component :display_device
component :chip
component :input_device
def initialize(device_id, data_resource)
@id = device_id
@data_resource = data_resource
end
end
You can drive it with something like:
class DataResource
def method_missing(method, *args)
# puts "called #{method} with:#{args.inspect}"
"#{method}-#{args.join(':')}"
end
end
device = Device.new("ID123", DataResource.new)
puts device.display_device
puts device.chip
puts device.input_device
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With