alias_metrics 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ tmp
2
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format Fuubar
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ group :development do
4
+ gem 'rspec', '~> 2.0'
5
+ gem 'fuubar', "~> 1.0.0"
6
+ gem "rake", "~> 0.9.2.2"
7
+ end
8
+
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ fuubar (1.0.0)
6
+ rspec (~> 2.0)
7
+ rspec-instafail (~> 0.2.0)
8
+ ruby-progressbar (~> 0.0.10)
9
+ rake (0.9.2.2)
10
+ rspec (2.8.0)
11
+ rspec-core (~> 2.8.0)
12
+ rspec-expectations (~> 2.8.0)
13
+ rspec-mocks (~> 2.8.0)
14
+ rspec-core (2.8.0)
15
+ rspec-expectations (2.8.0)
16
+ diff-lcs (~> 1.1.2)
17
+ rspec-instafail (0.2.2)
18
+ rspec-mocks (2.8.0)
19
+ ruby-progressbar (0.0.10)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ fuubar (~> 1.0.0)
26
+ rake (~> 0.9.2.2)
27
+ rspec (~> 2.0)
data/README ADDED
@@ -0,0 +1,49 @@
1
+ This tool is to visualize alias usage to parse command history. You can evaluate whether you use alias efficiently or not.
2
+ * It can show reduced types and more redusable types
3
+ * It can show how degree you use each alias
4
+
5
+ The visabliable shell scripts is the followings:
6
+ * zsh (${HOME}/.zsh-history)
7
+
8
+
9
+ How to use:
10
+
11
+ $ alias | alias_metrics
12
+
13
+
14
+ Case Study:
15
+
16
+ $ alias | alias_metrics
17
+ >>
18
+
19
+ You reduce 10.25% types (29814 / 290970)
20
+
21
+ If you use alias all, you can reduce more 3.98% types (11575 / 290970)
22
+
23
+
24
+
25
+ You often forget the following alias
26
+
27
+ alias #used #forgot forgot rate(%) command
28
+
29
+ g 9 1530 99.42 git
30
+
31
+ _ 0 210 100.00 sudo
32
+
33
+ gco 24 197 89.14 git checkout
34
+
35
+ ga 516 190 26.91 git add
36
+
37
+ ...
38
+ <<
39
+ I regist alias g=`git`, but I often forgot to use this alias. This result show I typed "git" 1530 times. So I can reduce 1530 * 2 = 3060 types by typing "g" instead of "git".
40
+
41
+
42
+ Todo:
43
+
44
+ * It can parse other shell scrpt(bash, csh, etc...)
45
+ * Add the function that recommends alias that can reduce your types by setting it
46
+ * Add the function that show Gold Standard
47
+ * Acceleration
48
+
49
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.ruby_opts = '-w'
6
+ end
7
+
8
+ task :default => :spec
9
+ rescue LoadError
10
+ raise 'RSpec could not be loaded. Run `bundle install` to get all development dependencies.'
11
+ end
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'alias_metrics'
3
+ spec.version = '0.1'
4
+ spec.summary = "This tool is to visualize alias usage to parse command history. You can evaluate whether you use alias efficiently or not."
5
+ spec.author = ["Kohei Tomita"]
6
+ spec.email = "tommy.fmale@gmail.com"
7
+ spec.executables = %w(alias_metrics)
8
+ spec.files = `git ls-files`.split("\n") rescue ''
9
+ spec.homepage = "https://github.com/tomity/alias_metrics"
10
+ spec.add_development_dependency 'rspec', '~> 2.0'
11
+ spec.add_development_dependency 'fuubar', "~> 1.0.0"
12
+ spec.add_development_dependency "rake", "~> 0.9.2.2"
13
+ end
data/bin/alias_metrics ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.dirname(__FILE__) + '/../lib/'
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require "alias_metrics"
5
+
6
+ @alias_list = AliasList.load_from_stdin
7
+ @history = CommandHistory.load_from_zsh_history(@alias_list)
8
+ command_length_all = @history.commands.map{|command| command.size}.inject(0){|partial_sum, size| partial_sum + size}
9
+ shorten_count = @history.shorten_count
10
+ shorten_rate = shorten_count.to_f / command_length_all
11
+ shortenable_count_all = @history.shortenables.values.inject(0){|partial_sum, shortenable| partial_sum + shortenable.count * [0, (shortenable.command.size - shortenable.alias.size)].max }
12
+ shortenable_rate = shortenable_count_all.to_f / command_length_all
13
+
14
+ puts "You reduce #{sprintf("%0.2f", shorten_rate * 100)}% types (#{shorten_count} types/ #{command_length_all} types)"
15
+ puts "If you use alias all, you can reduce more #{sprintf("%0.2f", shortenable_rate * 100)}% types (#{shortenable_count_all} types / #{command_length_all} types)"
16
+ puts
17
+
18
+ puts "You often forget the following alias"
19
+ puts "alias\t#used\t#forgot\tforgot rate(%)\tcommand"
20
+ keys = @history.shortenables.keys.sort{|a, b| @history.shortenables[a].count <=> @history.shortenables[b].count }.reverse
21
+ keys.each do |key|
22
+ shortenable = @history.shortenables[key]
23
+ alias_usage = @history.alias_usages[shortenable.alias]
24
+ forgot_rate = shortenable.count.to_f / (alias_usage.count + shortenable.count)
25
+ puts "#{shortenable.alias}\t#{alias_usage.count}\t#{shortenable.count}\t#{sprintf("%0.2f", forgot_rate*100)}\t#{shortenable.command}"
26
+ end
27
+
Binary file
@@ -0,0 +1,4 @@
1
+ require "alias_metrics/alias_list.rb"
2
+ require "alias_metrics/alias_usage.rb"
3
+ require "alias_metrics/command_history.rb"
4
+ require "alias_metrics/shortenable.rb"
@@ -0,0 +1,89 @@
1
+ class AliasList
2
+ attr_accessor :alias_hash
3
+
4
+ def self.load_from_lines(lines)
5
+ alias_hash = Hash::new
6
+ lines.each do |line|
7
+ key, value = separate_key_value_from_alias_line(line)
8
+ alias_hash[key] = value
9
+ end
10
+ AliasList.new(alias_hash)
11
+ end
12
+
13
+ def self.load_from_stdin
14
+ alias_hash = Hash::new
15
+ STDIN.each do |line|
16
+ line.chomp!
17
+ key, value = separate_key_value_from_alias_line(line)
18
+ alias_hash[key] = value
19
+ end
20
+ AliasList.new(alias_hash)
21
+ end
22
+
23
+ #NOTE: Since #command >> #alias, this process do fastly
24
+ def expand_command(command)
25
+ @alias_hash.each_pair do |key, value|
26
+ if used_subcommand?(command, key)
27
+ command = command.sub(key, value)
28
+ end
29
+ end
30
+ command
31
+ end
32
+
33
+ #NOTE: Since #command >> #alias, this process do fastly
34
+ def applied_alias(command)
35
+ ret = []
36
+ @alias_hash.each_pair do |key, value|
37
+ if used_subcommand?(command, key)
38
+ command = command.sub(key, value)
39
+ ret << [key, value]
40
+ end
41
+ end
42
+ ret
43
+ end
44
+
45
+ #NOTE: Since #command >> #alias, this process do fastly
46
+ def shortenable?(command)
47
+ @alias_hash.values.each do |value|
48
+ if used_subcommand?(command, value)
49
+ return true
50
+ end
51
+ end
52
+ false
53
+ end
54
+
55
+ def shortenable_alias(command)
56
+ ret = []
57
+ @alias_hash.each_pair do |key, value|
58
+ if used_subcommand?(command, value)
59
+ ret << [key, value]
60
+ end
61
+ end
62
+ ret
63
+ end
64
+
65
+ private
66
+
67
+ def initialize(alias_hash)
68
+ @alias_hash = alias_hash.freeze
69
+ end
70
+
71
+ def self.separate_key_value_from_alias_line(line)
72
+ key, value = line.split(/=/)
73
+ value = remove_single_quotes(value)
74
+ [key, value]
75
+ end
76
+
77
+ def self.remove_single_quotes(value)
78
+ if value[0, 1] == "'" and value[-1, 1] == "'"
79
+ value = value[1..-2]
80
+ end
81
+ value
82
+ end
83
+
84
+ def used_subcommand?(command, alias_)
85
+ command == alias_ || command.start_with?(alias_+ " ")
86
+ end
87
+ end
88
+
89
+
@@ -0,0 +1,13 @@
1
+
2
+ class AliasUsage
3
+ attr_accessor :alias
4
+ attr_accessor :command
5
+ attr_accessor :count
6
+
7
+ def initialize(alias_, command)
8
+ self.alias = alias_.freeze
9
+ self.command = command.freeze
10
+ self.count = 0
11
+ end
12
+
13
+ end
@@ -0,0 +1,79 @@
1
+ class CommandHistory
2
+ attr_accessor :commands
3
+ attr_accessor :shorten_count
4
+ attr_accessor :shortenables
5
+ attr_accessor :alias_usages
6
+ attr_accessor :alias_list
7
+
8
+ ZSH_HISTORY_FILE = "#{ENV["HOME"]}/.zsh_history"
9
+
10
+ def initialize(commands, alias_list)
11
+ self.alias_list = alias_list
12
+ self.commands = []
13
+ self.shorten_count = 0
14
+ self.shortenables = Hash.new
15
+ self.alias_usages = Hash.new
16
+ alias_list.alias_hash.each_pair do |alias_, value|
17
+ self.alias_usages[alias_] = AliasUsage.new(alias_, value)
18
+ self.shortenables[value] = Shortenable.new(alias_, value)
19
+ end
20
+
21
+ commands.each do |command|
22
+ update_shortenables(command)
23
+ update_alias_usages(command)
24
+ command_expanded = self.alias_list.expand_command(command)
25
+ self.shorten_count += [0, command_expanded.length - command.length].max
26
+ self.commands << command_expanded
27
+ end
28
+ self.alias_list = self.alias_list.freeze
29
+ self.commands = self.commands.freeze
30
+ self.shorten_count = self.shorten_count.freeze
31
+ self.shortenables = self.shortenables.freeze
32
+ self.alias_usages = self.alias_usages.freeze
33
+ end
34
+
35
+ def self.load_from_zsh_history(alias_list, history_file = ZSH_HISTORY_FILE)
36
+ commands = []
37
+ open(history_file) do |fh|
38
+ fh.each do |line|
39
+ line.chomp!
40
+ next if line == ""
41
+ begin
42
+ command = parse_command_by_zsh_history_line(line)
43
+ commands << command if command
44
+ rescue ArgumentError #invalid byte sequence in UTF-8
45
+ #do nothing
46
+ end
47
+ end
48
+ end
49
+ CommandHistory.new(commands, alias_list)
50
+ end
51
+
52
+ private
53
+
54
+ def self.parse_command_by_zsh_history_line(line)
55
+ command = line.split(/;/)[1]
56
+ return command
57
+ end
58
+
59
+ def update_shortenables(command)
60
+ if alias_list.shortenable?(command)
61
+ shortenable_alias_list = alias_list.shortenable_alias(command)
62
+ shortenable_alias_list.each do |alias_, extension|
63
+ self.shortenables[extension].count += 1 if shortenable?(alias_, extension)
64
+ end
65
+ end
66
+ end
67
+
68
+ def update_alias_usages(command)
69
+ applied_alias = self.alias_list.applied_alias(command)
70
+ applied_alias.each do |alias_, value|
71
+ self.alias_usages[alias_].count += 1
72
+ end
73
+ end
74
+
75
+ def shortenable?(alias_, command)
76
+ command.size > alias_.size
77
+ end
78
+
79
+ end
@@ -0,0 +1,11 @@
1
+ class Shortenable
2
+ attr_accessor :command
3
+ attr_accessor :alias
4
+ attr_accessor :count
5
+
6
+ def initialize(alias_, command)
7
+ self.alias = alias_.freeze
8
+ self.command = command.freeze
9
+ self.count = 0
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ require "alias_metrics"
2
+
3
+ describe AliasList do
4
+ before do
5
+ @alias_list = AliasList.load_from_lines(["l='ls -la'", "sl=ls", "grhh='git reset --hard HEAD\^'"])
6
+ end
7
+ describe "expand_command" do
8
+ it "should output `ls -la` for the command `l` " do
9
+ @alias_list.expand_command("l").should == "ls -la"
10
+ end
11
+
12
+ it "should output `ls -la -h` for the command `l -h` " do
13
+ @alias_list.expand_command("l -h").should == "ls -la -h"
14
+ end
15
+
16
+ it "should output `ls` for the command `sl` " do
17
+ @alias_list.expand_command("sl").should == "ls"
18
+ end
19
+
20
+ it "should output `ls -l` for the command `sl -l` " do
21
+ @alias_list.expand_command("sl -l").should == "ls -l"
22
+ end
23
+
24
+ it "should output `slope` for the command `slope` because this command is slope, but is not sl" do
25
+ @alias_list.expand_command("slope").should == "slope"
26
+ end
27
+ end
28
+
29
+ describe "shortenable" do
30
+ it "should output `ls -la` is shortenable" do
31
+ @alias_list.shortenable?("ls -la").should == true
32
+ end
33
+
34
+ it "should output `ls -la -h` for the command `l -h` " do
35
+ @alias_list.shortenable?("ls -la -h").should == true
36
+ end
37
+
38
+ it "should output `git reset --hard` is not shortenable" do
39
+ @alias_list.shortenable?("git reset --hard").should == false
40
+ end
41
+
42
+ it "should output `git HEAD\^ --hard` is not shortenable" do #Is this specification correct?
43
+ @alias_list.shortenable?("git reset HEAD\^ --hard").should == false
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+
50
+
@@ -0,0 +1,70 @@
1
+ require "alias_metrics"
2
+
3
+ describe CommandHistory do
4
+ before do
5
+ @alias_list = AliasList.load_from_lines(["l='ls -la'", "ga=git add", "run-help=man"])
6
+ @history = CommandHistory.new(["l", "l -h", "ls -la", "ls -la -h", "man"], @alias_list)
7
+ end
8
+
9
+ it "can get the number of commands" do
10
+ @history.commands.size.should == 5
11
+ end
12
+
13
+ it "can count the number of chars shorten" do
14
+ @history.shorten_count.should == 10 #becaulse (`ls -la` => `l`) * 2
15
+ end
16
+
17
+ it "should be count is 2 when ls -la" do
18
+ shortenable = @history.shortenables["ls -la"]
19
+ shortenable.alias.should == "l"
20
+ shortenable.command.should == "ls -la"
21
+ shortenable.count.should == 2
22
+ end
23
+
24
+ it "should be count is 0 when git add" do
25
+ shortenable = @history.shortenables["git add"]
26
+ shortenable.alias.should == "ga"
27
+ shortenable.command.should == "git add"
28
+ shortenable.count.should == 0
29
+ end
30
+
31
+ it "should be count is 0 when ls -la because run-help is longer than man" do
32
+ shortenable = @history.shortenables["man"]
33
+ shortenable.alias.should == "run-help"
34
+ shortenable.command.should == "man"
35
+ shortenable.count.should == 0
36
+ end
37
+
38
+ it "should be alias usage is 2 when l" do
39
+ alias_usage = @history.alias_usages["l"]
40
+ alias_usage.alias.should == "l"
41
+ alias_usage.command.should == "ls -la"
42
+ alias_usage.count.should == 2
43
+ end
44
+
45
+ it "should be alias usage is 0 when ga" do
46
+ alias_usage = @history.alias_usages["ga"]
47
+ alias_usage.alias.should == "ga"
48
+ alias_usage.command.should == "git add"
49
+ alias_usage.count.should == 0
50
+ end
51
+
52
+ it "should be alias usage is 0 when run-help" do
53
+ alias_usage = @history.alias_usages["run-help"]
54
+ alias_usage.alias.should == "run-help"
55
+ alias_usage.command.should == "man"
56
+ alias_usage.count.should == 0
57
+ end
58
+
59
+ it "can load from ~/.zsh_history" do
60
+ CommandHistory.load_from_zsh_history(@alias_list)
61
+ end
62
+
63
+ it "should store expanded commands" do
64
+ @history.commands[0].should == "ls -la"
65
+ @history.commands[1].should == "ls -la -h"
66
+ @history.commands[2].should == "ls -la"
67
+ @history.commands[3].should == "ls -la -h"
68
+ @history.commands[4].should == "man"
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alias_metrics
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kohei Tomita
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2152591180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2152591180
25
+ - !ruby/object:Gem::Dependency
26
+ name: fuubar
27
+ requirement: &2152590180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152590180
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2152608520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.2.2
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152608520
47
+ description:
48
+ email: tommy.fmale@gmail.com
49
+ executables:
50
+ - alias_metrics
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - Gemfile
57
+ - Gemfile.lock
58
+ - README
59
+ - Rakefile
60
+ - alias_metrics.gemspec
61
+ - bin/alias_metrics
62
+ - gems/alias_metrics-0.1.gem
63
+ - lib/alias_metrics.rb
64
+ - lib/alias_metrics/alias_list.rb
65
+ - lib/alias_metrics/alias_usage.rb
66
+ - lib/alias_metrics/command_history.rb
67
+ - lib/alias_metrics/shortenable.rb
68
+ - spec/alias_list_spec.rb
69
+ - spec/command_history_spec.rb
70
+ homepage: https://github.com/tomity/alias_metrics
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.10
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: This tool is to visualize alias usage to parse command history. You can evaluate
94
+ whether you use alias efficiently or not.
95
+ test_files: []