metric_tools 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +5 -0
- data/Gemfile.lock +29 -0
- data/README.md +13 -0
- data/lib/metric_tools.rb +9 -0
- data/lib/metric_tools/index_tree.rb +78 -0
- data/lib/metric_tools/s3.rb +87 -0
- data/lib/metric_tools/skype.rb +68 -0
- data/lib/metric_tools/spread_sheet.rb +108 -0
- data/lib/metric_tools/table.rb +244 -0
- metadata +53 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
faraday (0.8.4)
|
5
|
+
multipart-post (~> 1.1)
|
6
|
+
google_drive (0.3.2)
|
7
|
+
nokogiri (>= 1.4.4, != 1.5.2, != 1.5.1)
|
8
|
+
oauth (>= 0.3.6)
|
9
|
+
oauth2 (>= 0.5.0)
|
10
|
+
httpauth (0.2.0)
|
11
|
+
jwt (0.1.5)
|
12
|
+
multi_json (>= 1.0)
|
13
|
+
multi_json (1.5.0)
|
14
|
+
multipart-post (1.1.5)
|
15
|
+
nokogiri (1.5.6)
|
16
|
+
oauth (0.4.7)
|
17
|
+
oauth2 (0.8.0)
|
18
|
+
faraday (~> 0.8)
|
19
|
+
httpauth (~> 0.1)
|
20
|
+
jwt (~> 0.1.4)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
rack (~> 1.2)
|
23
|
+
rack (1.5.1)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
google_drive (~> 0)
|
data/README.md
ADDED
data/lib/metric_tools.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
root_path = File.expand_path("#{File.dirname(__FILE__)}/../")
|
2
|
+
require File.join(root_path, "config", "pref.rb")
|
3
|
+
|
4
|
+
lib_path = File.join(File.dirname(__FILE__), "/metric_tools")
|
5
|
+
Dir["#{File.join(lib_path, '/*.rb')}"].each {|f| require f }
|
6
|
+
|
7
|
+
module MetricTools
|
8
|
+
include Pref
|
9
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module MetricTools
|
4
|
+
|
5
|
+
#
|
6
|
+
# Tree構造のHash
|
7
|
+
#
|
8
|
+
class IndexTree
|
9
|
+
|
10
|
+
def initialize(hash_or_json=nil)
|
11
|
+
if hash_or_json.kind_of? Hash
|
12
|
+
build_with_hash(hash_or_json)
|
13
|
+
elsif hash_or_json.kind_of? String
|
14
|
+
build_with_json(hash_or_json)
|
15
|
+
else
|
16
|
+
@_hash = {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def [](*args)
|
22
|
+
ret = nil
|
23
|
+
process_tracing_keys(@_hash, args) {|obj, key, is_leaf|
|
24
|
+
ret = obj[key] if is_leaf
|
25
|
+
}
|
26
|
+
ret
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(*args)
|
30
|
+
val = args.pop
|
31
|
+
process_tracing_keys(@_hash, args) {|obj, key, is_leaf|
|
32
|
+
if is_leaf
|
33
|
+
obj[key] = val
|
34
|
+
else
|
35
|
+
obj[key] ||= {}
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear
|
41
|
+
@_hash = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
@_hash.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_json(option={})
|
49
|
+
if option[:pretty]
|
50
|
+
JSON.pretty_generate(@_hash)
|
51
|
+
else
|
52
|
+
JSON.generate(@_hash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
def build_with_hash(hash)
|
58
|
+
@_hash = hash
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_with_json(json)
|
62
|
+
@_hash = JSON.parse(json)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def process_tracing_keys(obj, keys, &proc)
|
67
|
+
current_obj = obj
|
68
|
+
keys.each_with_index{|current_key, index|
|
69
|
+
break if current_obj.nil?
|
70
|
+
|
71
|
+
proc.call(current_obj, current_key, index == keys.length-1)
|
72
|
+
current_obj = current_obj[current_key]
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'aws/s3'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module MetricTools
|
5
|
+
|
6
|
+
#
|
7
|
+
# S3からログなどをDownloadするためのクラスです
|
8
|
+
#
|
9
|
+
class S3
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
public
|
14
|
+
#
|
15
|
+
# access_key, secret_key, bucket_nameを指定してログインします。
|
16
|
+
#
|
17
|
+
def login(key, secret, bucket_name)
|
18
|
+
@key = key
|
19
|
+
@secret = secret
|
20
|
+
@bucket_name = bucket_name
|
21
|
+
|
22
|
+
AWS::S3::Base.establish_connection!(
|
23
|
+
:access_key_id => key,
|
24
|
+
:secret_access_key => secret,
|
25
|
+
)
|
26
|
+
@bucket = AWS::S3::Service.buckets.find{|b| b.name == @bucket_name}
|
27
|
+
raise "The bucket named '#{@bucket_name}' doesn't exist." if @bucket.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# loginしているかどうかを返します
|
32
|
+
#
|
33
|
+
def login?
|
34
|
+
return !bucket.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# file_keyを指定してファイルをダウンロードします。
|
39
|
+
# target_dirが存在しない場合は、勝手に作成します。
|
40
|
+
# hierarchy=trueにすると、S3上のディレクトリ構造を保持してDownloadします。
|
41
|
+
#
|
42
|
+
def download(file_key, target_dir, hierarchy=false)
|
43
|
+
file_path = File.join(target_dir, hierarchy ? file_key : File.basename(file_key))
|
44
|
+
mkdir_for_file(file_path)
|
45
|
+
puts "Downloading from S3 : #{file_key} => #{file_path}"
|
46
|
+
|
47
|
+
File.open(file_path, "w") {|fp|
|
48
|
+
AWS::S3::S3Object.stream(file_key, @bucket.name) {|chunk|
|
49
|
+
fp.write chunk
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# 指定されたdir_key以下のファイル/ディレクトリをすべてDLします。
|
56
|
+
# force_all=falseにすると、既にローカルにファイルが有る場合はDLしません。
|
57
|
+
#
|
58
|
+
def fetch_all(dir_key, target_dir, force_all=false, &condition)
|
59
|
+
remote_files = @bucket
|
60
|
+
.select{|obj|
|
61
|
+
key = obj.key
|
62
|
+
is_dir = (key[key.length-1] == '/')
|
63
|
+
name = key.include? dir_key
|
64
|
+
cond = condition.nil? ? true : condition.call(obj)
|
65
|
+
!is_dir && name && cond }
|
66
|
+
.collect{|obj| obj.key }
|
67
|
+
local_files = Dir.glob(File.join(target_dir, "**/*"))
|
68
|
+
|
69
|
+
remote_files.each {|key|
|
70
|
+
if force_all || !local_files.include?(File.basename(key))
|
71
|
+
download(key, target_dir, true)
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def mkdir_for_file(file_path)
|
79
|
+
dir_path = File.dirname(file_path)
|
80
|
+
FileUtils.mkdir_p(dir_path) unless File.exists?(dir_path)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rb-skypemac'
|
2
|
+
|
3
|
+
module MetricTools
|
4
|
+
|
5
|
+
#
|
6
|
+
# Skypeのチャットにメッセージをpostするクラスです。
|
7
|
+
# 使用するMac上でSkype for Mac OSXが起動していて、
|
8
|
+
# 対象のチャットルームに発言できるアカウントでログインされている必要があります。
|
9
|
+
#
|
10
|
+
class Skype
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
#
|
15
|
+
# nameで指定したチャットルームに発言出来る状態にします。
|
16
|
+
# login_chat("[GREE] パンカク連絡チャット")
|
17
|
+
#
|
18
|
+
def login_chat(name)
|
19
|
+
chats = SkypeMac::Skype.send_(:command => 'SEARCH RECENTCHATS')
|
20
|
+
chats = chats.sub("CHATS ","").split(/,/)
|
21
|
+
|
22
|
+
for id in chats
|
23
|
+
chat_info = SkypeMac::Skype.send_(:command => 'GET CHAT %s FRIENDLYNAME' % id).split(' ')
|
24
|
+
friendly_name = chat_info[3]
|
25
|
+
|
26
|
+
if friendly_name.include?(name)
|
27
|
+
@chat_id = chat_info[1]
|
28
|
+
@chat_name = friendly_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
raise "not existing chat room : #{name}" if @chat_id.nil?
|
33
|
+
@chat_id
|
34
|
+
end
|
35
|
+
|
36
|
+
def logout
|
37
|
+
@chat_id = nil
|
38
|
+
@chat_name = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# 現在のチャットルームのidを返します
|
43
|
+
#
|
44
|
+
def current_chat_id
|
45
|
+
@chat_id
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# 現在のチャットルームの表示名を返します
|
50
|
+
#
|
51
|
+
def current_chat_name
|
52
|
+
@chat_name
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# 現在のチャットルームに発言します
|
57
|
+
#
|
58
|
+
def post(message)
|
59
|
+
return puts 'please login before.' if current_chat_id.nil?
|
60
|
+
|
61
|
+
SkypeMac::Skype.send_(:command => "CHATMESSAGE #{current_chat_id} #{message}");
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
|
3
|
+
module MetricTools
|
4
|
+
|
5
|
+
#
|
6
|
+
# Google DocsのSpreadSheetを更新するためのクラス
|
7
|
+
# (gem google_driveのラッパーです)
|
8
|
+
#
|
9
|
+
class SpreadSheet
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :mail, :password, :key
|
13
|
+
|
14
|
+
# session lifetime
|
15
|
+
|
16
|
+
#
|
17
|
+
# mail, passwordでログインし, 該当するkeyのspreadsheetを編集可能にします
|
18
|
+
#
|
19
|
+
def login(mail, password, key)
|
20
|
+
@mail = mail
|
21
|
+
@password = password
|
22
|
+
@key = key
|
23
|
+
|
24
|
+
@session = GoogleDrive.login(@mail, @password)
|
25
|
+
@spread_sheet = @session.spreadsheet_by_key(@key)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# ログアウトします
|
30
|
+
#
|
31
|
+
def logout
|
32
|
+
@session = nil
|
33
|
+
@spread_sheet = nil
|
34
|
+
@worksheet = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# ログイン状態かどうかを返します
|
39
|
+
#
|
40
|
+
def login?
|
41
|
+
return !@session.nil? && !@spread_sheet.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
#worksheet
|
45
|
+
|
46
|
+
def current_worksheet_title
|
47
|
+
return nil if @worksheet.nil?
|
48
|
+
|
49
|
+
@worksheet.title
|
50
|
+
end
|
51
|
+
|
52
|
+
def change_worksheet_by_title(title)
|
53
|
+
@worksheet = @spread_sheet.worksheet_by_title(title)
|
54
|
+
@worksheet = @spread_sheet.add_worksheet(title) if @worksheet.nil?
|
55
|
+
|
56
|
+
@worksheet
|
57
|
+
end
|
58
|
+
|
59
|
+
# editing
|
60
|
+
|
61
|
+
#
|
62
|
+
# インデクサを使用して値にアクセスします
|
63
|
+
#
|
64
|
+
def [](i, j)
|
65
|
+
check_worksheet_seleced_or_raise_error()
|
66
|
+
|
67
|
+
return @worksheet[i+1, j+1]
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# インデクサを使用して値をアサインします
|
72
|
+
#
|
73
|
+
def []=(i, j, val)
|
74
|
+
check_worksheet_seleced_or_raise_error()
|
75
|
+
|
76
|
+
return @worksheet[i+1, j+1] = val
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Tableクラスの内容を、スプレッドシートに書き出します。
|
81
|
+
#
|
82
|
+
def set_value_table(table)
|
83
|
+
check_worksheet_seleced_or_raise_error()
|
84
|
+
|
85
|
+
raise 'can receive Table class only' unless table.kind_of? MetricTools::Table
|
86
|
+
table.each_with_index{|cell, i, j|
|
87
|
+
self[i, j] = cell
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# スプレッドシートにここまでの変更を反映します。
|
93
|
+
# saveを呼ぶまでは、実際にスプレッドシートには書き込まれません。
|
94
|
+
#
|
95
|
+
def save
|
96
|
+
check_worksheet_seleced_or_raise_error()
|
97
|
+
|
98
|
+
@worksheet.synchronize
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_worksheet_seleced_or_raise_error
|
102
|
+
raise "call 'change_worksheet_by_title' before." if @worksheet.nil?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'Hirb'
|
2
|
+
|
3
|
+
module MetricTools
|
4
|
+
|
5
|
+
#
|
6
|
+
# 2次元配列のデータ構造を簡単に扱うためのクラス
|
7
|
+
#
|
8
|
+
class Table
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@_inner_data = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Tableクラスのインスタンスを生成します
|
16
|
+
#
|
17
|
+
# 表の幅、高さを指定し、内容をブロックで指定します。
|
18
|
+
# ブロックの返り値が、表の(i, j)の位置に入ります。
|
19
|
+
#
|
20
|
+
# Table.create(10, 5) {|i, j|
|
21
|
+
# return "set to (#{i}, #{j})"
|
22
|
+
# }
|
23
|
+
def create(rows, cols, &proc)
|
24
|
+
table = Table.new
|
25
|
+
table.build(rows, cols, &proc)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Tableクラスのインスタンスを生成します
|
29
|
+
#
|
30
|
+
# 2次元配列を元にTableクラスに変換します。
|
31
|
+
#
|
32
|
+
# array = [[1, 2, 3], [4, 5, 6], [9, 0, '#']]
|
33
|
+
# Table.create_with_array(array)
|
34
|
+
def create_with_array(array)
|
35
|
+
Table.create(array[0].length, array.length) {|i, j|
|
36
|
+
array[j][i]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
public
|
42
|
+
#
|
43
|
+
# インデクサを用いてアクセスします
|
44
|
+
# 表の大きさを超えていると nilが返ります。
|
45
|
+
#
|
46
|
+
# table[1, 10]
|
47
|
+
def [](i, j)
|
48
|
+
@_inner_data[index_to_key(i, j)]
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# インデクサを用いてアクセスします
|
53
|
+
# 表の大きさを超えているとき、自動的に拡張します
|
54
|
+
#
|
55
|
+
# table[1, 10] = val
|
56
|
+
def []=(i, j, val)
|
57
|
+
raise "can't specify negative number for position." if i < 0 || j < 0
|
58
|
+
|
59
|
+
@_inner_data[index_to_key(i, j)] = val
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# tableの幅(列数)を取得します
|
64
|
+
#
|
65
|
+
def width
|
66
|
+
max = 0
|
67
|
+
for key in @_inner_data.keys
|
68
|
+
i, j = key_to_index(key)
|
69
|
+
max = j if max < j
|
70
|
+
end
|
71
|
+
return max + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# tableの幅(列数)を取得します
|
76
|
+
#
|
77
|
+
def cols
|
78
|
+
self.width
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# tableの高さ(行数)を取得します
|
83
|
+
#
|
84
|
+
def height
|
85
|
+
max = 0
|
86
|
+
for key in @_inner_data.keys
|
87
|
+
i, j = key_to_index(key)
|
88
|
+
max = i if max < i
|
89
|
+
end
|
90
|
+
return max + 1
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# tableの高さ(行数)を取得します
|
95
|
+
#
|
96
|
+
def rows
|
97
|
+
self.height
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_below(table)
|
101
|
+
table.each_with_index{|cell, i, j|
|
102
|
+
self[i, j+self.height] = cell
|
103
|
+
}
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# 2次元配列を返します。自身は変更されません。
|
109
|
+
#
|
110
|
+
# table.to_a => [[1,2,3], [4,5,6], [9,0,'#']]
|
111
|
+
def to_a
|
112
|
+
ret = []
|
113
|
+
@_inner_data.each{|key, val|
|
114
|
+
i, j = key_to_index(key)
|
115
|
+
|
116
|
+
ret[i] ||= []
|
117
|
+
ret[i][j] = val
|
118
|
+
}
|
119
|
+
ret
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# 全データに対して処理を行います
|
124
|
+
#
|
125
|
+
# table.each {|cell|
|
126
|
+
# cell += 10
|
127
|
+
# }
|
128
|
+
def each(&proc)
|
129
|
+
@_inner_data.keys.each{|key|
|
130
|
+
proc.call(@_inner_data[key])
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# 全データに対して処理を行います。(i, j)にデータの座標が入ります。
|
136
|
+
#
|
137
|
+
# table.each {|cell, i, j|
|
138
|
+
# cell = i + j * table.width
|
139
|
+
# }
|
140
|
+
def each_with_index(&proc)
|
141
|
+
@_inner_data.keys.each{|key|
|
142
|
+
i, j = key_to_index(key)
|
143
|
+
proc.call(@_inner_data[key], i, j)
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def collect(&proc)
|
148
|
+
new_one = Table.create(self.rows, self.cols){|i, j|
|
149
|
+
proc.call(self[i, j], i, j)
|
150
|
+
}
|
151
|
+
new_one
|
152
|
+
end
|
153
|
+
|
154
|
+
# output console methods
|
155
|
+
|
156
|
+
#
|
157
|
+
# コンソールに綺麗な表形式で出力します(Hirbを使用)
|
158
|
+
#
|
159
|
+
def print_pretty
|
160
|
+
puts Hirb::Helpers::Table.render @data, :headers => @headers, :resize => false
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# コンソールにcsv形式で出力します
|
165
|
+
#
|
166
|
+
def print_csv
|
167
|
+
print_with_seperator(",")
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# コンソールにtsv形式で出力します
|
172
|
+
#
|
173
|
+
def print_for_docs
|
174
|
+
print_with_seperator("\t")
|
175
|
+
end
|
176
|
+
|
177
|
+
def print_with_seperator(s)
|
178
|
+
to_a.each{|row|
|
179
|
+
puts row.join(s)
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# csvファイルとして出力します
|
185
|
+
#
|
186
|
+
def output_csv_file(path)
|
187
|
+
output_file_with_seperator(",", path)
|
188
|
+
end
|
189
|
+
|
190
|
+
#
|
191
|
+
# tsvファイルとして出力します
|
192
|
+
#
|
193
|
+
def output_tsv_file(path)
|
194
|
+
output_file_with_seperator("\t", path)
|
195
|
+
end
|
196
|
+
|
197
|
+
def output_file_with_seperator(s, path)
|
198
|
+
puts "start output >> #{path}..."
|
199
|
+
|
200
|
+
ret = ''
|
201
|
+
to_a.each{|row|
|
202
|
+
ret << row.join(s) + "\n"
|
203
|
+
}
|
204
|
+
|
205
|
+
File.open(path, 'w') {|fp|
|
206
|
+
fp.write ret
|
207
|
+
}
|
208
|
+
puts "finish"
|
209
|
+
end
|
210
|
+
|
211
|
+
def build(rows, cols, &proc)
|
212
|
+
for i in 0..rows-1
|
213
|
+
for j in 0..cols-1
|
214
|
+
@_inner_data[index_to_key(i, j)] = proc.call(i, j)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
self
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def index_to_key(i, j)
|
224
|
+
"#{i}-#{j}"
|
225
|
+
end
|
226
|
+
|
227
|
+
def key_to_index(key)
|
228
|
+
i, j = key.split('-')
|
229
|
+
[i.to_i, j.to_i]
|
230
|
+
end
|
231
|
+
|
232
|
+
def move_cell(i, j, delta_x, delta_y)
|
233
|
+
raise "can't specify negative number for position." if i < 0 || j < 0
|
234
|
+
raise "can't move to negative position." if i + delta_x < 0 || j + delta_y < 0
|
235
|
+
|
236
|
+
src_key = index_to_key(i, j)
|
237
|
+
dst_key = index_to_key(i + delta_x, j + delta_y)
|
238
|
+
|
239
|
+
@_inner_data[dst_key] = @_inner_data[src_key]
|
240
|
+
@_inner_data.delete(src_key)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metric_tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Masaori Hirono
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2010-04-28 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Utilities for analyzing metrics
|
15
|
+
email: masaori@pankaku.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/metric_tools/index_tree.rb
|
21
|
+
- lib/metric_tools/s3.rb
|
22
|
+
- lib/metric_tools/skype.rb
|
23
|
+
- lib/metric_tools/spread_sheet.rb
|
24
|
+
- lib/metric_tools/table.rb
|
25
|
+
- lib/metric_tools.rb
|
26
|
+
- Gemfile
|
27
|
+
- Gemfile.lock
|
28
|
+
- README.md
|
29
|
+
homepage: http://google.com/
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.25
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Utilities for analyzing metrics
|
53
|
+
test_files: []
|