dropcaster 0.0.8 → 1.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/Rakefile CHANGED
@@ -1,16 +1,19 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rake/testtask'
5
5
  require 'rake/clean'
6
+ require 'rubocop/rake_task'
6
7
  require 'dropcaster'
7
8
 
9
+ RuboCop::RakeTask.new
10
+
8
11
  Rake::TestTask.new(:test) do |test|
9
12
  test.libs << 'test'
10
13
  test.pattern = 'test/**/test_*.rb'
11
14
  end
12
15
 
13
- task :default => :test
16
+ task default: %i[rubocop test]
14
17
 
15
18
  namespace :web do
16
19
  file 'website/index.markdown' do |f|
@@ -35,8 +38,8 @@ namespace :web do
35
38
  end
36
39
  CLOBBER << 'website/contributing.md'
37
40
 
38
- desc "Generate web page"
39
- task :generate => ['website/index.markdown', 'website/vision.markdown', 'website/contributing.md'] do
41
+ desc 'Generate web page'
42
+ task generate: ['website/index.markdown', 'website/vision.markdown', 'website/contributing.md'] do
40
43
  cd 'website' do
41
44
  `bundle exec jekyll build`
42
45
  end
data/Vagrantfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # -*- mode: ruby -*-
2
4
  # vi: set ft=ruby :
3
5
 
data/bin/dropcaster CHANGED
@@ -1,22 +1,24 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
5
 
5
6
  require 'yaml'
7
+ require 'English'
6
8
 
7
- help = <<HELP
8
- Dropcaster is a podcast feed generator for the command line.
9
+ help = <<~HELP
10
+ Dropcaster is a podcast feed generator for the command line.
9
11
 
10
- Author: Nicolas E. Rabenau nerab@gmx.at
11
- Homepage: http://nerab.github.io/dropcaster/
12
+ Author: Nicolas E. Rabenau nerab@gmx.at
13
+ Homepage: http://nerab.github.io/dropcaster/
12
14
 
13
- Basic Usage:
15
+ Basic Usage:
14
16
 
15
- dropcaster Prints a podcast feed document for the mp3 files in the current directory.
16
- dropcaster [FILE]... Prints a podcast feed document for FILES
17
- dropcaster [DIR]... Prints a podcast feed document for the mp3 files in DIR
17
+ dropcaster Prints a podcast feed document for the mp3 files in the current directory.
18
+ dropcaster [FILE]... Prints a podcast feed document for FILES
19
+ dropcaster [DIR]... Prints a podcast feed document for the mp3 files in DIR
18
20
 
19
- Options:
21
+ Options:
20
22
  HELP
21
23
 
22
24
  def usage
@@ -25,105 +27,105 @@ end
25
27
 
26
28
  require 'optparse'
27
29
  require 'dropcaster'
30
+ require 'dropcaster/log_formatter'
28
31
 
29
- options = Hash.new
32
+ options = {}
30
33
  options[:auto_detect_channel_file] = true
31
34
 
32
- opts = OptionParser.new do |opts|
35
+ logger = Logger.new(STDERR).tap do |l|
36
+ l.level = Logger::WARN
37
+ l.formatter = Dropcaster::LogFormatter.new
38
+ end
39
+
40
+ # rubocop:disable Metrics/BlockLength
41
+ OptionParser.new do |opts|
33
42
  opts.banner = help
34
43
 
35
- opts.on("--verbose", "Verbose mode - displays additional diagnostic information") do |file|
36
- Dropcaster.logger = Logger.new(STDERR)
37
- Dropcaster.logger.formatter = Dropcaster::LogFormatter.new
38
- Dropcaster.logger.level = Logger::INFO
44
+ opts.on('--verbose', 'Verbose mode - displays additional diagnostic information') do |file|
45
+ logger.level = Logger::INFO
39
46
  end
40
47
 
41
- opts.on("--trace", "Verbose mode - displays additional diagnostic information") do |file|
42
- Dropcaster.logger = Logger.new(STDERR)
43
- Dropcaster.logger.formatter = Dropcaster::LogFormatter.new
44
- Dropcaster.logger.level = Logger::DEBUG
48
+ opts.on('--trace', 'Verbose mode - displays additional diagnostic information') do |file|
49
+ logger.level = Logger::DEBUG
45
50
  end
46
51
 
47
- opts.on("--channel FILE", "Read the channel definition from FILE instead of channel.yml in the current directory.") do |file|
48
- begin
49
- Dropcaster.logger.info "Reading channel definition from #{file}"
50
- options = YAML.load_file(file).merge(options)
51
- options[:auto_detect_channel_file] = false
52
- rescue
53
- Dropcaster.logger.error "Could not load channel definition. #{$!.message}"
54
- Dropcaster.logger.info $!.backtrace
55
- exit(1)
56
- end
52
+ opts.on('--channel FILE', 'Read the channel definition from FILE instead of channel.yml in the current directory.') do |file|
53
+ logger.info "Reading channel definition from #{file}"
54
+ options = YAML.load_file(file).merge(options)
55
+ options[:auto_detect_channel_file] = false
56
+ rescue StandardError
57
+ logger.error "Could not load channel definition. #{$ERROR_INFO.message}"
58
+ logger.info $ERROR_INFO.backtrace
59
+ exit(1)
57
60
  end
58
61
 
59
- opts.on("--title STRING", "Use STRING as the channel's title. Overrides settings read from channel definition file.") do |title|
60
- Dropcaster.logger.info "Setting channel title to '#{title}' via command line"
62
+ opts.on('--title STRING', "Use STRING as the channel's title. Overrides settings read from channel definition file.") do |title|
63
+ logger.info "Setting channel title to '#{title}' via command line"
61
64
  options[:title] = title
62
65
  end
63
66
 
64
- opts.on("--subtitle STRING", "Use STRING as the channel's subtitle. Overrides settings read from channel definition file.") do |subtitle|
65
- Dropcaster.logger.info "Setting channel subtitle to '#{subtitle}' via command line"
67
+ opts.on('--subtitle STRING', "Use STRING as the channel's subtitle. Overrides settings read from channel definition file.") do |subtitle|
68
+ logger.info "Setting channel subtitle to '#{subtitle}' via command line"
66
69
  options[:subtitle] = subtitle
67
70
  end
68
71
 
69
- opts.on("--url URL", "Use URL as the channel's url. Overrides settings read from channel definition file.") do |url|
70
- Dropcaster.logger.info "Setting channel URL to '#{url}' via command line"
72
+ opts.on('--url URL', "Use URL as the channel's url. Overrides settings read from channel definition file.") do |url|
73
+ logger.info "Setting channel URL to '#{url}' via command line"
71
74
  options[:url] = url
72
75
  end
73
76
 
74
- opts.on("--description STRING", "Use STRING as the channel's description. Overrides settings read from channel definition file.") do |description|
75
- Dropcaster.logger.info "Setting channel description to '#{description}' via command line"
77
+ opts.on('--description STRING', "Use STRING as the channel's description. Overrides settings read from channel definition file.") do |description|
78
+ logger.info "Setting channel description to '#{description}' via command line"
76
79
  options[:description] = description
77
80
  end
78
81
 
79
- opts.on("--enclosures URL", "Use URL as the base URL for the channel's enclosures. Overrides settings read from channel definition file.") do |enclosures_url|
80
- Dropcaster.logger.info "Setting enclosures base URL to '#{enclosures_url}' via command line"
82
+ opts.on('--enclosures URL', "Use URL as the base URL for the channel's enclosures. Overrides settings read from channel definition file.") do |enclosures_url|
83
+ logger.info "Setting enclosures base URL to '#{enclosures_url}' via command line"
81
84
  options[:enclosures_url] = enclosures_url
82
85
  end
83
86
 
84
- opts.on("--image URL", "Use URL as the channel's image URL. Overrides settings read from channel definition file.") do |image_url|
85
- Dropcaster.logger.info "Setting image URL to '#{image_url}' via command line"
87
+ opts.on('--image URL', "Use URL as the channel's image URL. Overrides settings read from channel definition file.") do |image_url|
88
+ logger.info "Setting image URL to '#{image_url}' via command line"
86
89
  options[:image_url] = image_url
87
90
  end
88
91
 
89
- opts.on("--channel-template FILE", "Use FILE as template for generating the channel feed. Overrides the default that comes with Dropcaster.") do |file|
90
- Dropcaster.logger.info "Using'#{file}' as channel template file"
92
+ opts.on('--channel-template FILE', 'Use FILE as template for generating the channel feed. Overrides the default that comes with Dropcaster.') do |file|
93
+ logger.info "Using'#{file}' as channel template file"
91
94
  options[:channel_template] = file
92
95
  end
93
96
 
94
- opts.on("--version", "Display current version") do
97
+ opts.on('--version', 'Display current version') do
95
98
  puts "#{File.basename(__FILE__)} " + Dropcaster::VERSION
96
99
  exit 0
97
100
  end
98
- end
101
+ end.parse!
102
+ # rubocop:enable Metrics/BlockLength
99
103
 
100
- opts.parse!
101
104
  sources = ARGV.blank? ? '.' : ARGV
102
105
 
103
106
  if options[:auto_detect_channel_file]
104
107
  # There was no channel file specified, so we try to load channel.yml from sources dir
105
108
  channel_file = Dropcaster::ChannelFileLocator.locate(sources)
106
109
 
107
- if File.exists?(channel_file)
108
- Dropcaster.logger.info "Auto-detected channel file at #{channel_file}"
110
+ if File.exist?(channel_file)
111
+ logger.info "Auto-detected channel file at #{channel_file}"
109
112
  options_from_yaml = YAML.load_file(channel_file)
110
113
  options = options_from_yaml.merge(options)
111
114
  else
112
- Dropcaster.logger.error "No channel file found at #{channel_file})"
113
- Dropcaster.logger.info usage
115
+ logger.error "No channel file found at #{channel_file})"
116
+ logger.info usage
114
117
  exit(1) # No way to continue without a channel definition
115
118
  end
116
119
  end
117
120
 
118
- Dropcaster.logger.info "Generating the channel with these options: #{options.inspect}"
119
-
121
+ logger.info "Generating the channel with these options: #{options.inspect}"
120
122
  begin
121
123
  puts Dropcaster::Channel.new(sources, options).to_rss
122
- rescue
123
- Dropcaster.logger.error $!.message
124
- $!.backtrace.each do |line|
125
- Dropcaster.logger.debug(line)
124
+ rescue StandardError
125
+ logger.error $ERROR_INFO.message
126
+ $ERROR_INFO.backtrace.each do |line|
127
+ logger.debug(line)
126
128
  end
127
- Dropcaster.logger.info usage
129
+ logger.info usage
128
130
  exit(1)
129
131
  end
data/bin/lstags CHANGED
@@ -1,51 +1,54 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  unless ARGV.size == 1
4
5
  STDERR.puts "#{File.basename(__FILE__)}: Missing required parameter for the mp3 file to process"
5
6
  exit(1)
6
7
  end
7
8
 
8
- require 'rubygems'
9
9
  require 'mp3info'
10
-
11
10
  begin
12
11
  file_name = ARGV.first
13
12
 
14
13
  puts "Listing tags for file: #{file_name}"
15
14
 
15
+ # rubocop:disable Metrics/BlockLength
16
+ # rubocop:disable Lint/EmptyWhen
16
17
  Mp3Info.open(file_name) do |mp3info|
17
18
  puts 'ID3v1 tags:'
18
- mp3info.tag.keys.each{|key|
19
+ mp3info.tag.each_key { |key|
19
20
  puts " #{key} => #{mp3info.tag.send(key)}"
20
21
  }
21
22
  puts
22
23
  puts 'ID3v2 tags:'
23
- mp3info.tag2.keys.each{|key|
24
+ mp3info.tag2.each_key { |key|
24
25
  case key
25
- when 'PIC'
26
- when 'APIC'
27
- # picture - do not print binary data
28
- when 'ULT'
29
- print " ULT => "
30
- block_counter = 0
31
- mp3info.tag2.ULT.bytes{|b|
32
- print "0x%02x " % b.to_i
33
- print b > 31 ? " '#{b.chr}' " : " " * 5
34
- if (block_counter += 1) > 7 # display in blocks of 8 bytes
35
- puts
36
- print " " * 9
37
- block_counter = 0
38
- end
39
- }
40
- puts
41
- else
42
- puts " #{key} => #{mp3info.tag2.send(key)}"
26
+ when 'PIC'
27
+ when 'APIC'
28
+ # picture - do not print binary data
29
+ when 'ULT'
30
+ print ' ULT => '
31
+ block_counter = 0
32
+ mp3info.tag2.ULT.bytes { |b|
33
+ print sprintf('0x%02x ', b.to_i)
34
+ print b > 31 ? " '#{b.chr}' " : ' ' * 5
35
+ if (block_counter += 1) > 7 # display in blocks of 8 bytes
36
+ puts
37
+ print ' ' * 9
38
+ block_counter = 0
39
+ end
40
+ }
41
+ puts
42
+ else
43
+ puts " #{key} => #{mp3info.tag2.send(key)}"
43
44
  end
44
45
  }
45
46
  end
46
- puts "Modification date: #{File.new(file_name).mtime}"
47
+ # rubocop:enable Metrics/BlockLength
48
+ # rubocop:enable Lint/EmptyWhen
47
49
 
48
- rescue
49
- puts "Error: #{$!.message}"
50
+ puts "Modification date: #{File.new(file_name).mtime}"
51
+ rescue StandardError
52
+ puts "Error: #{$ERROR_INFO.message}"
50
53
  exit(1)
51
54
  end
data/dropcaster.gemspec CHANGED
@@ -1,10 +1,10 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
5
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
5
  require 'dropcaster/version'
7
6
 
7
+ # rubocop:disable Metrics/BlockLength
8
8
  Gem::Specification.new do |spec|
9
9
  spec.name = 'dropcaster'
10
10
  spec.version = Dropcaster::VERSION
@@ -20,22 +20,24 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'ruby-mp3info'
24
23
  spec.add_dependency 'activesupport'
25
24
  spec.add_dependency 'bundler'
25
+ spec.add_dependency 'nokogiri', '~> 1.8.2'
26
+ spec.add_dependency 'null-logger'
27
+ spec.add_dependency 'ruby-mp3info'
26
28
 
27
- spec.add_development_dependency 'minitest'
28
- spec.add_development_dependency 'rake'
29
- spec.add_development_dependency 'libxml-ruby'
30
- spec.add_development_dependency 'guard-minitest'
29
+ spec.add_development_dependency 'github-pages'
31
30
  spec.add_development_dependency 'guard-bundler'
31
+ spec.add_development_dependency 'guard-minitest'
32
32
  spec.add_development_dependency 'libnotify'
33
- spec.add_development_dependency 'rb-inotify'
34
- spec.add_development_dependency 'rb-fsevent'
33
+ spec.add_development_dependency 'libxml-ruby'
34
+ spec.add_development_dependency 'minitest'
35
+ spec.add_development_dependency 'octokit'
35
36
  spec.add_development_dependency 'pry'
36
37
  spec.add_development_dependency 'pry-byebug'
37
- spec.add_development_dependency 'rb-readline'
38
- spec.add_development_dependency 'github-pages'
39
- spec.add_development_dependency 'octokit'
38
+ spec.add_development_dependency 'rake'
39
+ spec.add_development_dependency 'rb-fsevent'
40
+ spec.add_development_dependency 'rb-inotify'
40
41
  spec.add_development_dependency 'rubocop'
41
42
  end
43
+ # rubocop:enable Metrics/BlockLength
@@ -1,14 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
  require 'uri'
5
+ require 'dropcaster/logging'
3
6
 
4
7
  module Dropcaster
5
8
  #
6
9
  # Represents a podcast feed in the RSS 2.0 format
7
10
  #
8
11
  class Channel
12
+ include Logging
9
13
  include ERB::Util # for h() in the ERB template
10
14
 
11
- STORAGE_UNITS = %w(Byte KB MB GB TB)
15
+ STORAGE_UNITS = %w[Byte KB MB GB TB].freeze
12
16
  MAX_KEYWORD_COUNT = 12
13
17
 
14
18
  # Instantiate a new Channel object. +sources+ must be present and can be a String or Array
@@ -23,13 +27,13 @@ module Dropcaster
23
27
  #
24
28
  def initialize(sources, attributes)
25
29
  # Assert mandatory attributes
26
- [:title, :url, :description].each{|attr|
30
+ %i[title url description].each { |attr|
27
31
  raise MissingAttributeError.new(attr) if attributes[attr].blank?
28
32
  }
29
33
 
30
34
  @attributes = attributes
31
35
  @attributes[:explicit] = yes_no_or_input(attributes[:explicit])
32
- @source_files = Array.new
36
+ @source_files = []
33
37
 
34
38
  # if (sources.respond_to?(:each)) # array
35
39
  if sources.is_a? Array
@@ -43,13 +47,13 @@ module Dropcaster
43
47
 
44
48
  # If not absolute, prepend the image URL with the channel's base to make an absolute URL
45
49
  unless @attributes[:image_url].blank? || @attributes[:image_url] =~ /^https?:/
46
- Dropcaster.logger.info("Channel image URL '#{@attributes[:image_url]}' is relative, so we prepend it with the channel URL '#{@attributes[:url]}'")
50
+ logger.info("Channel image URL '#{@attributes[:image_url]}' is relative, so we prepend it with the channel URL '#{@attributes[:url]}'")
47
51
  @attributes[:image_url] = (URI.parse(@attributes[:url]) + @attributes[:image_url]).to_s
48
52
  end
49
53
 
50
54
  # If enclosures_url is not given, take the channel URL as a base.
51
55
  if @attributes[:enclosures_url].blank?
52
- Dropcaster.logger.info("No enclosure URL given, using the channel's enclosure URL")
56
+ logger.info("No enclosure URL given, using the channel's enclosure URL")
53
57
  @attributes[:enclosures_url] = @attributes[:url]
54
58
  end
55
59
 
@@ -59,7 +63,7 @@ module Dropcaster
59
63
  channel_template = @attributes[:channel_template] || File.join(File.dirname(__FILE__), '..', '..', 'templates', 'channel.rss.erb')
60
64
 
61
65
  begin
62
- @erb_template = ERB.new(IO.read(channel_template), 0, "%<>")
66
+ @erb_template = ERB.new(IO.read(channel_template), 0, '%<>')
63
67
  rescue Errno::ENOENT => e
64
68
  raise TemplateNotFoundError.new(e.message)
65
69
  end
@@ -78,26 +82,25 @@ module Dropcaster
78
82
  # Returns all items (episodes) of this channel, ordered by newest-first.
79
83
  #
80
84
  def items
81
- all_items = Array.new
82
- @source_files.each{|src|
83
-
85
+ all_items = []
86
+ @source_files.each { |src|
84
87
  item = Item.new(src)
85
88
 
86
- Dropcaster.logger.debug("Adding new item from file #{src}")
89
+ logger.debug("Adding new item from file #{src}")
87
90
 
88
91
  # set author and image_url from channel if empty
89
92
  if item.artist.blank?
90
- Dropcaster.logger.info("#{src} has no artist, using the channel's author")
93
+ logger.info("#{src} has no artist, using the channel's author")
91
94
  item.tag.artist = @attributes[:author]
92
95
  end
93
96
 
94
97
  if item.image_url.blank?
95
- Dropcaster.logger.info("#{src} has no image URL set, using the channel's image URL")
98
+ logger.info("#{src} has no image URL set, using the channel's image URL")
96
99
  item.image_url = @attributes[:image_url]
97
100
  end
98
101
 
99
102
  # construct absolute URL, based on the channel's enclosures_url attribute
100
- item.url = (URI.parse(enclosures_url) + URI.encode(item.file_name))
103
+ item.url = URI.parse(enclosures_url) + item.file_path.each_filename.map { |p| url_encode(p) }.join('/')
101
104
 
102
105
  # Warn if keyword count is larger than recommended
103
106
  assert_keyword_count(item.keywords)
@@ -105,13 +108,13 @@ module Dropcaster
105
108
  all_items << item
106
109
  }
107
110
 
108
- all_items.sort{|x, y| y.pub_date <=> x.pub_date}
111
+ all_items.sort { |x, y| y.pub_date <=> x.pub_date }
109
112
  end
110
113
 
111
114
  # from http://stackoverflow.com/questions/4136248
112
115
  def humanize_time(secs)
113
- [[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
114
- if secs > 0
116
+ [[60, :s], [60, :m], [24, :h], [1000, :d]].map { |count, name|
117
+ if secs.positive?
115
118
  secs, n = secs.divmod(count)
116
119
  "#{n.to_i}#{name}"
117
120
  end
@@ -122,37 +125,43 @@ module Dropcaster
122
125
  def humanize_size(number)
123
126
  return nil if number.nil?
124
127
 
125
- storage_units_format = '%n %u'
126
-
127
128
  if number.to_i < 1024
128
129
  unit = number > 1 ? 'Bytes' : 'Byte'
129
- return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
130
130
  else
131
131
  max_exp = STORAGE_UNITS.size - 1
132
132
  number = Float(number)
133
133
  exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
134
134
  exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
135
- number /= 1024 ** exponent
136
-
135
+ number /= 1024**exponent
137
136
  unit = STORAGE_UNITS[exponent]
138
- return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
139
137
  end
138
+
139
+ '%n %u'.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
140
140
  end
141
141
 
142
- # delegate all unknown methods to @attributes
142
+ #
143
+ # Delegate all unknown methods to @attributes
144
+ #
145
+ # rubocop:disable Style/MethodMissing
143
146
  def method_missing(meth, *args)
144
147
  m = meth.id2name
145
- if /=$/ =~ m
148
+ if /=$/.match?(m)
146
149
  @attributes[m.chop.to_sym] = (args.length < 2 ? args[0] : args)
147
150
  else
148
151
  @attributes[m.to_sym]
149
152
  end
150
153
  end
154
+ # rubocop:enable Style/MethodMissing
155
+
156
+ def respond_to_missing?(meth, *)
157
+ /=$/.match?(meth.id2name) || super
158
+ end
159
+
160
+ private
151
161
 
152
- private
153
162
  def add_files(src)
154
163
  if File.directory?(src)
155
- @source_files.concat(Dir.glob(File.join(src, '*.mp3')))
164
+ @source_files.concat(Dir.glob(File.join(src, '*.mp3'), File::FNM_CASEFOLD))
156
165
  else
157
166
  @source_files << src
158
167
  end
@@ -163,12 +172,12 @@ module Dropcaster
163
172
  #
164
173
  def yes_no_or_input(flag)
165
174
  case flag
166
- when nil
167
- nil
168
- when true
169
- 'Yes'
170
- when false
171
- 'No'
175
+ when nil
176
+ nil
177
+ when true
178
+ 'Yes'
179
+ when false
180
+ 'No'
172
181
  else
173
182
  flag
174
183
  end
@@ -177,19 +186,21 @@ module Dropcaster
177
186
  #
178
187
  # http://snippets.dzone.com/posts/show/4578
179
188
  #
180
- def truncate(string, count = 30)
181
- if string.length >= count
182
- shortened = string[0, count]
183
- splitted = shortened.split(/\s/)
184
- words = splitted.length
185
- splitted[0, words - 1].join(' ') + '...'
186
- else
187
- string
188
- end
189
+ def truncate(string, count=30)
190
+ if string.length >= count
191
+ shortened = string[0, count]
192
+ splitted = shortened.split(/\s/)
193
+ words = splitted.length
194
+ splitted[0, words - 1].join(' ') + '...'
195
+ else
196
+ string
197
+ end
189
198
  end
190
199
 
191
200
  def assert_keyword_count(keywords)
192
- Dropcaster.logger.info("The list of keywords has #{keywords.size} entries, which exceeds the recommended maximum of #{MAX_KEYWORD_COUNT}.") if keywords && MAX_KEYWORD_COUNT < keywords.size
201
+ if keywords && MAX_KEYWORD_COUNT < keywords.size
202
+ logger.info("The list of keywords has #{keywords.size} entries, which exceeds the recommended maximum of #{MAX_KEYWORD_COUNT}.")
203
+ end
193
204
  end
194
205
  end
195
206
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dropcaster
2
4
  #
3
5
  # Encapsulates the strategy how to find the channel definition file
@@ -27,9 +29,9 @@ module Dropcaster
27
29
 
28
30
  if sources.respond_to?(:at)
29
31
  # More than one source given. Check that they are all in the same directory.
30
- distinct_dirs = sources.collect{|source| dir_or_self(source)}.uniq
32
+ distinct_dirs = sources.collect { |source| dir_or_self(source) }.uniq
31
33
 
32
- if 1 == distinct_dirs.size
34
+ if distinct_dirs.size == 1
33
35
  # If all are the in same directory, use that as source directory where channel.yml is expected.
34
36
  channel_source_dir = distinct_dirs.first
35
37
  else
@@ -40,11 +42,12 @@ module Dropcaster
40
42
  # If a single file or directory is given, use that as source directory where channel.yml is expected.
41
43
  channel_source_dir = dir_or_self(sources)
42
44
  end
43
-
45
+
44
46
  File.join(channel_source_dir, CHANNEL_YML)
45
47
  end
46
-
47
- private
48
+
49
+ private
50
+
48
51
  def dir_or_self(source)
49
52
  if File.directory?(source)
50
53
  source
@@ -1,12 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'octokit'
2
4
 
3
5
  module Dropcaster
4
- def self.contributors
5
- cbtors = Octokit.contributors('nerab/dropcaster', true)
6
+ class << self
7
+ def contributors
8
+ @octokit ||= if ENV.include?('GH_TOKEN')
9
+ Octokit::Client.new(access_token: ENV['GH_TOKEN'])
10
+ else
11
+ Octokit::Client.new
12
+ end
13
+
14
+ @octokit.contributors('nerab/dropcaster', true).
15
+ sort { |x, y| y.contributions <=> x.contributions }.
16
+ map { |c| "* #{contributor_summary(c)}" }.
17
+ join("\n")
18
+ end
6
19
 
7
- cbtors.sort!{|x,y| y.contributions <=> x.contributions }
8
- cbtors.map!{|c| "* [#{Octokit.user(c.login).name}](#{c.html_url}) (#{c.contributions} contributions)"}
20
+ def contributor_summary(contributor)
21
+ contributions = contributor.contributions
22
+ "#{contributor_link(contributor)} (#{contributions} contribution#{contributions == 1 ? '' : 's'})"
23
+ end
9
24
 
10
- cbtors.join("\n")
25
+ def contributor_link(contributor)
26
+ if contributor.type == 'Anonymous'
27
+ contributor.name.tr('[]', '()')
28
+ else
29
+ # rubocop:disable Style/RescueStandardError
30
+ begin
31
+ "[#{@octokit.user(contributor.login).name}](#{contributor.html_url})"
32
+ rescue
33
+ contributor.login
34
+ end
35
+ # rubocop:enable Style/RescueStandardError
36
+ end
37
+ end
11
38
  end
12
39
  end