how_is 8.0.0 → 9.0.0

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/lib/how_is/chart.rb CHANGED
@@ -1,83 +1,83 @@
1
- class HowIs::Chart
2
- # Generates the gnuplot script in data/issues.plg.
3
- #
4
- # Some configuration is available. Font locations are path to a TTF or other
5
- # Gnuplot-readable font name.
6
- #
7
- # For example that could be '/Users/anne/Library/Fonts/InputMono-Medium.ttf'
8
- # or just 'Helvetica'.
9
- #
10
- # @param font_location [String] Font for the chart
11
- # @param font_size [Integer] Size of the chart text
12
- # @param label_font_location [String] Font for labels
13
- # @param label_font_size [Integer] Size of the label text
14
- #
15
- # @return void
16
- def self.gnuplot(font_location: nil,
17
- font_size: 16,
18
- label_font_location: nil,
19
- label_font_size: 10,
20
- chartsize: '500,500',
21
- data_file:,
22
- png_file:)
23
- default_font_location =
24
- if Gem.win_platform?
25
- 'Arial'
26
- else
27
- 'Helvetica'
28
- end
29
-
30
- font_location ||= default_font_location
31
- label_font_location ||= font_location
32
-
33
- cmd = %Q{
34
- gnuplot -e "labelfont='#{label_font_location},#{label_font_size}'" \
35
- -e "chartfont='#{font_location},#{font_size}'" \
36
- -e "chartsize='#{chartsize}'" \
37
- -e "data='#{data_file}'" \
38
- -e "pngfile='#{png_file}'" \
39
- -c data/issues.plg
40
- }
41
- puts cmd
42
- IO.popen(cmd, 'w')
43
- end
44
-
45
- def self.rotate(offset, filename)
46
- if Gem.win_platform?
47
- rotate_with_dotnet(filename, offset)
48
- else
49
- rotate_with_minimagick(filename, offset)
50
- end
51
- end
52
-
53
- def self.rotate_with_dotnet(filename, offset)
54
- ps_rotate_flip = {
55
- 90 => 'Rotate90FlipNone',
56
- 180 => 'Rotate180FlipNone',
57
- 270 => 'Rotate270FlipNone',
58
- -90 => 'Rotate270FlipNone'
59
- }[offset]
60
-
61
- command = %Q{
62
- $path = "#{filename}"
63
-
64
- [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
65
- $i = new-object System.Drawing.Bitmap $path
66
-
67
- $i.RotateFlip("#{ps_rotate_flip}")
68
-
69
- $i.Save($path,"png")
70
-
71
- exit
72
- }
73
-
74
- IO.popen(["powershell", "-Command", command], 'w') { |io| }
75
- end
76
-
77
- def self.rotate_with_minimagick(filename, offset)
78
- require 'mini_magick'
79
- image = MiniMagick::Image.new(filename) { |b| b.rotate offset.to_s }
80
- image.format 'png'
81
- image.write filename
82
- end
83
- end
1
+ class HowIs::Chart
2
+ # Generates the gnuplot script in data/issues.plg.
3
+ #
4
+ # Some configuration is available. Font locations are path to a TTF or other
5
+ # Gnuplot-readable font name.
6
+ #
7
+ # For example that could be '/Users/anne/Library/Fonts/InputMono-Medium.ttf'
8
+ # or just 'Helvetica'.
9
+ #
10
+ # @param font_location [String] Font for the chart
11
+ # @param font_size [Integer] Size of the chart text
12
+ # @param label_font_location [String] Font for labels
13
+ # @param label_font_size [Integer] Size of the label text
14
+ #
15
+ # @return void
16
+ def self.gnuplot(font_location: nil,
17
+ font_size: 16,
18
+ label_font_location: nil,
19
+ label_font_size: 10,
20
+ chartsize: '500,500',
21
+ data_file:,
22
+ png_file:)
23
+ default_font_location =
24
+ if Gem.win_platform?
25
+ 'Arial'
26
+ else
27
+ 'Helvetica'
28
+ end
29
+
30
+ font_location ||= default_font_location
31
+ label_font_location ||= font_location
32
+
33
+ cmd = %Q{
34
+ gnuplot -e "labelfont='#{label_font_location},#{label_font_size}'" \
35
+ -e "chartfont='#{font_location},#{font_size}'" \
36
+ -e "chartsize='#{chartsize}'" \
37
+ -e "data='#{data_file}'" \
38
+ -e "pngfile='#{png_file}'" \
39
+ -c data/issues.plg
40
+ }
41
+ puts cmd
42
+ IO.popen(cmd, 'w')
43
+ end
44
+
45
+ def self.rotate(offset, filename)
46
+ if Gem.win_platform?
47
+ rotate_with_dotnet(filename, offset)
48
+ else
49
+ rotate_with_minimagick(filename, offset)
50
+ end
51
+ end
52
+
53
+ def self.rotate_with_dotnet(filename, offset)
54
+ ps_rotate_flip = {
55
+ 90 => 'Rotate90FlipNone',
56
+ 180 => 'Rotate180FlipNone',
57
+ 270 => 'Rotate270FlipNone',
58
+ -90 => 'Rotate270FlipNone'
59
+ }[offset]
60
+
61
+ command = %Q{
62
+ $path = "#{filename}"
63
+
64
+ [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
65
+ $i = new-object System.Drawing.Bitmap $path
66
+
67
+ $i.RotateFlip("#{ps_rotate_flip}")
68
+
69
+ $i.Save($path,"png")
70
+
71
+ exit
72
+ }
73
+
74
+ IO.popen(["powershell", "-Command", command], 'w') { |io| }
75
+ end
76
+
77
+ def self.rotate_with_minimagick(filename, offset)
78
+ require 'mini_magick'
79
+ image = MiniMagick::Image.new(filename) { |b| b.rotate offset.to_s }
80
+ image.format 'png'
81
+ image.write filename
82
+ end
83
+ end
data/lib/how_is/cli.rb CHANGED
@@ -1,92 +1,90 @@
1
- require 'how_is'
2
- require 'yaml'
3
- require 'contracts'
4
- require 'stringio'
5
-
6
- C = Contracts
7
-
8
- class HowIs::CLI
9
- include Contracts::Core
10
-
11
- DEFAULT_CONFIG_FILE = 'how_is.yml'
12
-
13
- # Generates YAML frontmatter, as is used in Jekyll and other blog engines.
14
- #
15
- # E.g.,
16
- # generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
17
- # => "---\nfoo: bar asdf\n"
18
- Contract C::HashOf[C::Or[String, Symbol] => String],
19
- C::HashOf[C::Or[String, Symbol] => C::Any] => String
20
- def generate_frontmatter(frontmatter, report_data)
21
- frontmatter = convert_keys(frontmatter, :to_s)
22
- report_data = convert_keys(report_data, :to_sym)
23
-
24
- frontmatter = frontmatter.map { |k, v|
25
- v = v % report_data
26
-
27
- [k, v]
28
- }.to_h
29
-
30
- YAML.dump(frontmatter)
31
- end
32
-
33
- def from_config_file(config_file = nil, **kwargs)
34
- config_file ||= DEFAULT_CONFIG_FILE
35
-
36
- from_config(YAML.load_file(config_file), **kwargs)
37
- end
38
-
39
- def from_config(config,
40
- github: nil,
41
- report_class: nil)
42
- report_class ||= HowIs::Report
43
-
44
- date = Date.strptime(Time.now.to_i.to_s, '%s')
45
- date_string = date.strftime('%Y-%m-%d')
46
- friendly_date = date.strftime('%B %d, %y')
47
-
48
- analysis = HowIs.generate_analysis(repository: config['repository'], github: github)
49
-
50
- report_data = {
51
- repository: config['repository'],
52
- date: date,
53
- friendly_date: friendly_date,
54
- }
55
-
56
- config['reports'].map do |format, report_config|
57
- filename = report_config['filename'] % report_data
58
- file = File.join(report_config['directory'], filename)
59
-
60
- report = report_class.export(analysis, format)
61
-
62
- result = build_report(report_config['frontmatter'], report_data, report)
63
-
64
- File.open(file, 'w') do |f|
65
- f.puts result
66
- end
67
-
68
- result
69
- end
70
- end
71
-
72
- def build_report(frontmatter, report_data, report)
73
- str = StringIO.new
74
-
75
- if frontmatter
76
- str.puts generate_frontmatter(frontmatter, report_data)
77
- str.puts "---"
78
- str.puts
79
- end
80
-
81
- str.puts report
82
-
83
- str.string
84
- end
85
-
86
- private
87
- # convert_keys({'foo' => 'bar'}, :to_sym)
88
- # => {:foo => 'bar'}
89
- def convert_keys(data, method_name)
90
- data.map {|k, v| [k.send(method_name), v]}.to_h
91
- end
92
- end
1
+ require 'how_is'
2
+ require 'yaml'
3
+ require 'contracts'
4
+ require 'stringio'
5
+
6
+ class HowIs::CLI
7
+ include Contracts::Core
8
+
9
+ DEFAULT_CONFIG_FILE = 'how_is.yml'
10
+
11
+ # Generates YAML frontmatter, as is used in Jekyll and other blog engines.
12
+ #
13
+ # E.g.,
14
+ # generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
15
+ # => "---\nfoo: bar asdf\n"
16
+ Contract C::HashOf[C::Or[String, Symbol] => String],
17
+ C::HashOf[C::Or[String, Symbol] => C::Any] => String
18
+ def generate_frontmatter(frontmatter, report_data)
19
+ frontmatter = convert_keys(frontmatter, :to_s)
20
+ report_data = convert_keys(report_data, :to_sym)
21
+
22
+ frontmatter = frontmatter.map { |k, v|
23
+ v = v % report_data
24
+
25
+ [k, v]
26
+ }.to_h
27
+
28
+ YAML.dump(frontmatter)
29
+ end
30
+
31
+ def from_config_file(config_file = nil, **kwargs)
32
+ config_file ||= DEFAULT_CONFIG_FILE
33
+
34
+ from_config(YAML.load_file(config_file), **kwargs)
35
+ end
36
+
37
+ def from_config(config,
38
+ github: nil,
39
+ report_class: nil)
40
+ report_class ||= HowIs::Report
41
+
42
+ date = Date.strptime(Time.now.to_i.to_s, '%s')
43
+ date_string = date.strftime('%Y-%m-%d')
44
+ friendly_date = date.strftime('%B %d, %y')
45
+
46
+ analysis = HowIs.generate_analysis(repository: config['repository'], github: github)
47
+
48
+ report_data = {
49
+ repository: config['repository'],
50
+ date: date,
51
+ friendly_date: friendly_date,
52
+ }
53
+
54
+ config['reports'].map do |format, report_config|
55
+ filename = report_config['filename'] % report_data
56
+ file = File.join(report_config['directory'], filename)
57
+
58
+ report = report_class.export(analysis, format)
59
+
60
+ result = build_report(report_config['frontmatter'], report_data, report)
61
+
62
+ File.open(file, 'w') do |f|
63
+ f.puts result
64
+ end
65
+
66
+ result
67
+ end
68
+ end
69
+
70
+ def build_report(frontmatter, report_data, report)
71
+ str = StringIO.new
72
+
73
+ if frontmatter
74
+ str.puts generate_frontmatter(frontmatter, report_data)
75
+ str.puts "---"
76
+ str.puts
77
+ end
78
+
79
+ str.puts report
80
+
81
+ str.string
82
+ end
83
+
84
+ private
85
+ # convert_keys({'foo' => 'bar'}, :to_sym)
86
+ # => {:foo => 'bar'}
87
+ def convert_keys(data, method_name)
88
+ data.map {|k, v| [k.send(method_name), v]}.to_h
89
+ end
90
+ end
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "how_is"
4
+ require "how_is/cli"
5
+ require "slop"
6
+
7
+ class HowIs::CLI
8
+ DEFAULT_REPORT_FILE = "report.#{HowIs::DEFAULT_FORMAT}"
9
+
10
+ class OptionsError < StandardError
11
+ end
12
+
13
+ class Parser
14
+ attr_reader :opts
15
+
16
+ def call(argv)
17
+ opts = Slop::Options.new
18
+ opts.banner =
19
+ <<-EOF.gsub(/ *\| ?/, '')
20
+ | Usage: how_is REPOSITORY [--report REPORT_FILE]
21
+ | how_is --config CONFIG_FILE
22
+ |
23
+ | Where REPOSITORY is of the format <GitHub username or org>/<repository name>.
24
+ | CONFIG_FILE defaults to how_is.yml.
25
+ |
26
+ | E.g., if you wanted to check https://github.com/how-is/how_is,
27
+ | you'd run `how_is how-is/how_is`.
28
+ |
29
+ EOF
30
+
31
+ opts.separator ""
32
+ opts.separator "Options:"
33
+
34
+ opts.bool "-h", "--help", "Print help text"
35
+ opts.string "--config", "YAML config file, used to generate a group of reports"
36
+ opts.string "--from", "JSON report file, used instead of fetching the data again"
37
+ opts.string "--report", "output file for the report (valid extensions: #{HowIs.supported_formats.join(', ')}; default: #{DEFAULT_REPORT_FILE})"
38
+ opts.string "-v", "--version", "prints the version"
39
+
40
+ parser = Slop::Parser.new(opts)
41
+ result = parser.parse(argv)
42
+ options = result.to_hash
43
+ arguments = result.arguments
44
+
45
+ options[:report] ||= DEFAULT_REPORT_FILE
46
+
47
+ # The following are only useful if true.
48
+ # Removing them here simplifies contracts and keyword args for other APIs.
49
+ options.delete(:config) unless options[:config]
50
+ options.delete(:help) unless options[:help]
51
+ options.delete(:version) unless options[:version]
52
+
53
+ unless HowIs.can_export_to?(options[:report])
54
+ raise OptionsError, "Invalid file: #{options[:report_file]}. Supported formats: #{HowIs.supported_formats.join(', ')}"
55
+ end
56
+
57
+ if options[:config]
58
+ # Nothing to do.
59
+ elsif options[:from]
60
+ # Opening this file here seems a bit messy, but it works.
61
+ options[:repository] = JSON.parse(open(options[:from_file]).read)['repository']
62
+ raise OptionsError, "Invalid JSON report file." unless options[:repository]
63
+ elsif argv.length >= 1
64
+ options[:repository] = argv.delete_at(0)
65
+ else
66
+ raise OptionsError, "No repository specified."
67
+ end
68
+
69
+ {
70
+ opts: opts,
71
+ options: options,
72
+ arguments: arguments,
73
+ }
74
+ end
75
+ end
76
+ end
@@ -1,45 +1,45 @@
1
- require 'contracts'
2
- require 'github_api'
3
-
4
- ##
5
- # Fetches data from GitHub.
6
- class HowIs::Fetcher
7
- include Contracts::Core
8
-
9
- ##
10
- # Standardized representation for fetcher results.
11
- #
12
- # Implemented as a class instead of passing around a Hash so that it can
13
- # be more easily referenced by Contracts.
14
- class Results < Struct.new(:repository, :issues, :pulls)
15
- include Contracts::Core
16
-
17
- Contract String, C::ArrayOf[Hash], C::ArrayOf[Hash] => nil
18
- def initialize(repository, issues, pulls)
19
- super(repository, issues, pulls)
20
- end
21
-
22
- # Struct defines #to_h, but not #to_hash, so we alias them.
23
- alias_method :to_hash, :to_h
24
- end
25
-
26
-
27
- Contract String, C::Or[C::RespondTo[:issues, :pulls], nil] => Results
28
- def call(repository,
29
- github = nil)
30
- github ||= Github.new(auto_pagination: true)
31
- user, repo = repository.split('/', 2)
32
- issues = github.issues.list user: user, repo: repo
33
- pulls = github.pulls.list user: user, repo: repo
34
-
35
- Results.new(
36
- repository,
37
- obj_to_array_of_hashes(issues),
38
- obj_to_array_of_hashes(pulls)
39
- )
40
- end
41
-
42
- private def obj_to_array_of_hashes(object)
43
- object.to_a.map(&:to_h)
44
- end
45
- end
1
+ require 'contracts'
2
+ require 'github_api'
3
+
4
+ ##
5
+ # Fetches data from GitHub.
6
+ class HowIs::Fetcher
7
+ include Contracts::Core
8
+
9
+ ##
10
+ # Standardized representation for fetcher results.
11
+ #
12
+ # Implemented as a class instead of passing around a Hash so that it can
13
+ # be more easily referenced by Contracts.
14
+ class Results < Struct.new(:repository, :issues, :pulls)
15
+ include Contracts::Core
16
+
17
+ Contract String, C::ArrayOf[Hash], C::ArrayOf[Hash] => nil
18
+ def initialize(repository, issues, pulls)
19
+ super(repository, issues, pulls)
20
+ end
21
+
22
+ # Struct defines #to_h, but not #to_hash, so we alias them.
23
+ alias_method :to_hash, :to_h
24
+ end
25
+
26
+
27
+ Contract String, C::Or[C::RespondTo[:issues, :pulls], nil] => Results
28
+ def call(repository,
29
+ github = nil)
30
+ github ||= Github.new(auto_pagination: true)
31
+ user, repo = repository.split('/', 2)
32
+ issues = github.issues.list user: user, repo: repo
33
+ pulls = github.pulls.list user: user, repo: repo
34
+
35
+ Results.new(
36
+ repository,
37
+ obj_to_array_of_hashes(issues),
38
+ obj_to_array_of_hashes(pulls)
39
+ )
40
+ end
41
+
42
+ private def obj_to_array_of_hashes(object)
43
+ object.to_a.map(&:to_h)
44
+ end
45
+ end