metaprogramming: use case and concern
Suppose we have a class like this
class Device
def latest_data
{
"data" => {
"node_info" => "this is a sensor",
"battery" => 90
},
"device_type" => "Sensor"
}
end
end
and we want to call the data by method
d = Device
p d.node_info
p d.battery
with metaprogramming we can write elegant methods like this
class Device
include ActsAsField
field :device_type, "device_type"
field :battery, "data.battery"
field :node_info, "data.node_info"
field :battery_to_text, proc { |device| "#{device.battery}%" }
def latest_data
{
"data" => {
"node_info" => "this is a sensor",
"battery" => 90
},
"device_type" => "Sensor"
}
end
end
Now lets see what this module ActsAsField looks like
check the answer
module ActsAsField
def self.included base
base.include InstanceMethods
base.extend ClassMethods
base.class_eval do
@@acts_as_fields = []
end
end
module ClassMethods
def field name, path
result = class_variable_get(:@@acts_as_fields)
result << name.to_sym
class_variable_set(:@@acts_as_fields, result)
define_method(name) do
case path
when String
path.split(".").inject(self.latest_data) { |data, key| data[key] }
when Proc
path.call(self)
end
end
end
end
module InstanceMethods
def acts_as_field
end
end
end
concern
Let’s look at this module ActsAsField
module ActsAsField
def self.included base
base.include InstanceMethods
base.extend ClassMethods
base.class_eval do
@@acts_as_fields = []
end
end
module ClassMethods
def class_method
end
# ...
end
module InstanceMethods
def instance_method
end
# ...
end
end
If we use active_support/concern, we can have code like this
require 'active_support/concern'
module ActsAsField
extend ActiveSupport::Concern
included do
@@acts_as_fields = []
end
class_methods do
def class_method
end
end
def instance_method
end
end