How can metaprogramming be used to reduce redundancy in this Ruby code?

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 above code, quite a lot of redundancy exists within the framework of the methods. Regardless of whether metaprogramming is the best way to reduce this redundancy, I hope to find out how to use metaprogramming in Ruby to reduce some repeatability here if someone can give some suggestions.

+4
source share
6 answers

Here is a version that uses metaprogramming, although I would also remove duplication by placing it in the method it is in.

 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 did not want to use the method for this function, you can simply replace the call to the resource_summary method with the body of the resource_summary method.

+6
source

Something like this might work, so you can define β€œcomponents” (or whatever) declaratively. This is an extra case for such an example, but you can use it when you need to define tens / hundreds of these things, or you put it as part of some structure (for example, rails).

The component -level method of the component class will usually work in some other module that is included in the class, rather than declaring it inline where it is used like that.

 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 manage 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 
+3
source

Obviously, some names must change ...

 def display_device i_heart_meta_programming("display_device") end def chip i_heart_meta_programming("chip") end def input_device i_heart_meta_programming("input_device") end def i_heart_meta_programming(what_to_get) mode = @data_resource.send("get_#{what_to_get}_mode", @id) mode = @data_resource.send("get_#{what_to_get}_presets", @id) summary = "#{what_to_get}: #{mode} ($#{presets})" return "* #{summary}" if presets == "XTC909" summary end 
+2
source

Are you sure you need to reduce redundancy here? This is certainly possible, but everything you do will simply make the code more difficult to understand and will not necessarily be a clear victory.

0
source

I assume you will probably solve this alreaday, anyway, this is my alternative:

 class Device def initialize(device_id, data_resource) @id,@data_resource = device_id, data_resource end %w{display_device chip input_device}.each do |met| define_method met do mode = @data_resource.send("get_#{met}_mode", @id) presets = @data_resource.send("get_#{met}_presets",@id) summary = "#{met}: #{mode} ($#{presets})" return "* #{summary}" if presets == "XTC909" summary end end end 
0
source

Can you find a better example?

As I said, your previous version of this, metaprogramming is hardly needed here. Basic encapsulation of functionality in methods will work.

Any examples that people give would be far-fetched and not really real metaprogramming activities in the real world.

-1
source

All Articles