- なんしよーとや
- さて、今回
— Metric Filter と Subscription Filter を取得するメソッド
— どこが冗長なん? - ということで、一本
— define_method を使ってメソッドを動的に生成することにした - あざっした
なんしよーとや
awspec の CloudWatch Logs Resource Type の Generator を作ろうとしています。
github.com
一応、Pull request 済みです。
github.com
さて、今回
Metric Filter と Subscription Filter を取得するメソッド
Log group をキーとして Metric Filter と Subscription Filter を取得するメソッドを以下のように書いていました。
def generate_log_metric_filters_specs(log_group) req = { log_group_name: log_group } metric_filters = [] loop do res = cloudwatch_logs_client.describe_metric_filters(req) metric_filters.push(*res.metric_filters) break if res.next_token.nil? req[:next_token] = res.next_token end metric_filter_lines = [] metric_filters.each do |metric_filter| line = "it { should have_metric_filter('#{metric_filter.filter_name}') }" metric_filter_lines.push(line) end metric_filter_lines end def generate_log_subscription_filters_specs(log_group) req = { log_group_name: log_group } subscription_filters = [] loop do res = cloudwatch_logs_client.describe_subscription_filters(req) subscription_filters.push(*res.subscription_filters) break if res.next_token.nil? req[:next_token] = res.next_token end subscription_filter_lines = [] subscription_filters.each do |subscription_filter| line = "it { should have_subscription_filter('#{subscription_filter.filter_name}')" unless subscription_filter.filter_pattern.empty? line += ".filter_pattern('#{subscription_filter.filter_pattern}')" end line += ' }' subscription_filter_lines.push(line) end subscription_filter_lines end
なんと無く冗長です。
どこが冗長なん?
インフラでは冗長化は当たり前の事かもしれませんが、プログラミングでは冗長はあまり好まれないと考えています。
ということで、個人的には以下の部分が冗長だと思っています。
... req = { log_group_name: log_group } metric_filters = [] loop do res = cloudwatch_logs_client.describe_metric_filters(req) metric_filters.push(*res.metric_filters) break if res.next_token.nil? req[:next_token] = res.next_token end ... req = { log_group_name: log_group } subscription_filters = [] loop do res = cloudwatch_logs_client.describe_subscription_filters(req) subscription_filters.push(*res.subscription_filters) break if res.next_token.nil? req[:next_token] = res.next_token end ...
これらの処理をうまーく、一つの処理にまとめられればメソッドの行数は減らせて冗長を解消出来そうです。
ということで、一本
define_method を使ってメソッドを動的に生成することにした
以下はサンプル実装です。
require 'aws-sdk' require 'pp' cloudwatch_logs_client = Aws::CloudWatchLogs::Client.new(profile: 'your-profile', region: 'ap-northeast-1') filter_types = %w(metric subscription) filter_types.each do |type| define_method 'select_all_cloudwatch_logs_' + type + '_filter' do |*args| req = { log_group_name: args.first } method_name = 'describe_' + type + '_filters' resources = [] loop do res = cloudwatch_logs_client.method(method_name).call(req) case type when 'metric' then resources.push(*res.metric_filters) when 'subscription' then resources.push(*res.subscription_filters) end break if res.next_token.nil? req[:next_token] = res.next_token end resources end end pp select_all_cloudwatch_logs_metric_filter('my_log_group_name') pp select_all_cloudwatch_logs_subscription_filter('my_log_group_name')
ポイントは define_method
ですな。実行すると以下のようなメソッドに展開されると妄想しています。
def select_all_cloudwatch_logs_metric_filter(log_group) req = { log_group_name: log_group } metric_filters = [] loop do res = cloudwatch_logs_client.describe_metric_filters(req) metric_filters.push(*res.metric_filters) break if res.next_token.nil? req[:next_token] = res.next_token end end ... def select_all_cloudwatch_logs_subscription_filter(log_group) req = { log_group_name: log_group } subscription_filters = [] loop do res = cloudwatch_logs_client.describe_subscription_filters(req) subscription_filters.push(*res.subscription_filters) break if res.next_token.nil? req[:next_token] = res.next_token end end
実際に実行してみると以下のように出力されました。
$ bundle exec ruby test.rb [#], creation_time=1491118453323, log_group_name="my_log_group_name">] [# ]
意図した通りの結果が返ってきています。
あざっした
メソッドの動的生成は初めての体験でした。define_method
を適材適所で使うことで柔軟で且つ少ないコードで処理を実装出来るって素晴らしいですね。
Ruby すごなあ。