dropcaster 0.0.4 → 0.0.5.rc1
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.
- checksums.yaml +7 -0
- data/.document +5 -5
- data/.gitignore +14 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -13
- data/Gemfile.lock +94 -29
- data/Guardfile +12 -0
- data/LICENSE.txt +20 -20
- data/{README.md → README.markdown} +185 -171
- data/Rakefile +11 -47
- data/TODO +13 -12
- data/bin/dropcaster +129 -117
- data/bin/lstags +51 -45
- data/doc/sample-channel.yml +88 -81
- data/doc/sample-sidecar.yml +19 -19
- data/dropcaster.gemspec +36 -97
- data/lib/dropcaster/channel.rb +195 -159
- data/lib/dropcaster/channel_file_locator.rb +57 -57
- data/lib/dropcaster/errors.rb +25 -25
- data/lib/dropcaster/item.rb +28 -47
- data/lib/dropcaster/log_formatter.rb +9 -9
- data/lib/dropcaster/version.rb +3 -0
- data/lib/dropcaster.rb +28 -30
- data/templates/channel.html.erb +45 -43
- data/templates/channel.rss.erb +65 -64
- data/test/extensions/windows.rb +12 -12
- data/test/fixtures/channel.yml +16 -15
- data/test/fixtures/test_template.json.erb +40 -40
- data/test/helper.rb +10 -14
- data/test/unit/test_app.rb +129 -99
- data/test/unit/test_channel.rb +80 -73
- data/test/unit/test_channel_locator.rb +76 -93
- data/test/unit/test_channel_xml.rb +90 -89
- data/test/unit/test_item.rb +51 -63
- metadata +225 -158
- data/VERSION +0 -1
- data/lib/dropcaster/hashkeys.rb +0 -12
data/dropcaster.gemspec
CHANGED
@@ -1,97 +1,36 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
"dropcaster.gemspec",
|
38
|
-
"lib/dropcaster.rb",
|
39
|
-
"lib/dropcaster/channel.rb",
|
40
|
-
"lib/dropcaster/channel_file_locator.rb",
|
41
|
-
"lib/dropcaster/errors.rb",
|
42
|
-
"lib/dropcaster/hashkeys.rb",
|
43
|
-
"lib/dropcaster/item.rb",
|
44
|
-
"lib/dropcaster/log_formatter.rb",
|
45
|
-
"templates/channel.html.erb",
|
46
|
-
"templates/channel.rss.erb",
|
47
|
-
"test/extensions/windows.rb",
|
48
|
-
"test/fixtures/channel.yml",
|
49
|
-
"test/fixtures/iTunes.mp3",
|
50
|
-
"test/fixtures/test_template.json.erb",
|
51
|
-
"test/helper.rb",
|
52
|
-
"test/unit/test_app.rb",
|
53
|
-
"test/unit/test_channel.rb",
|
54
|
-
"test/unit/test_channel_locator.rb",
|
55
|
-
"test/unit/test_channel_xml.rb",
|
56
|
-
"test/unit/test_item.rb"
|
57
|
-
]
|
58
|
-
s.homepage = %q{http://nerab.github.com/dropcaster}
|
59
|
-
s.licenses = ["MIT"]
|
60
|
-
s.require_paths = ["lib"]
|
61
|
-
s.rubygems_version = %q{1.6.1}
|
62
|
-
s.summary = %q{Simple Podcast Publishing with Dropbox}
|
63
|
-
|
64
|
-
if s.respond_to? :specification_version then
|
65
|
-
s.specification_version = 3
|
66
|
-
|
67
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
68
|
-
s.add_runtime_dependency(%q<ruby-mp3info>, [">= 0"])
|
69
|
-
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
70
|
-
s.add_development_dependency(%q<rake>, ["= 0.8.7"])
|
71
|
-
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
73
|
-
s.add_development_dependency(%q<libxml-ruby>, [">= 0"])
|
74
|
-
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
75
|
-
s.add_development_dependency(%q<json>, [">= 0"])
|
76
|
-
else
|
77
|
-
s.add_dependency(%q<ruby-mp3info>, [">= 0"])
|
78
|
-
s.add_dependency(%q<activesupport>, [">= 0"])
|
79
|
-
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
80
|
-
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
81
|
-
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
82
|
-
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
83
|
-
s.add_dependency(%q<rdoc>, [">= 0"])
|
84
|
-
s.add_dependency(%q<json>, [">= 0"])
|
85
|
-
end
|
86
|
-
else
|
87
|
-
s.add_dependency(%q<ruby-mp3info>, [">= 0"])
|
88
|
-
s.add_dependency(%q<activesupport>, [">= 0"])
|
89
|
-
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
90
|
-
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
91
|
-
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
92
|
-
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
93
|
-
s.add_dependency(%q<rdoc>, [">= 0"])
|
94
|
-
s.add_dependency(%q<json>, [">= 0"])
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dropcaster/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dropcaster"
|
8
|
+
spec.version = Dropcaster::VERSION
|
9
|
+
spec.authors = ["Nicholas E. Rabenau"]
|
10
|
+
spec.email = ["nerab@gmx.at"]
|
11
|
+
spec.summary = %q{Simple Podcast Publishing with Dropbox}
|
12
|
+
spec.description = %q{Dropcaster is a podcast feed generator for the command line. It is most simple to use with Dropbox, but works equally well with any other hoster.}
|
13
|
+
spec.homepage = "https://github.com/nerab/dropcaster"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'ruby-mp3info', '~> 0.8'
|
22
|
+
spec.add_dependency 'activesupport', '~> 3.2'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.1'
|
26
|
+
spec.add_development_dependency 'libxml-ruby', '~> 2.7'
|
27
|
+
spec.add_development_dependency 'guard-minitest', '~> 2.2'
|
28
|
+
spec.add_development_dependency 'guard-bundler', '~> 2.0'
|
29
|
+
spec.add_development_dependency 'libnotify', '~> 0.8'
|
30
|
+
spec.add_development_dependency 'rb-inotify', '~> 0.9'
|
31
|
+
spec.add_development_dependency 'rb-fsevent', '~> 0.9'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.9'
|
33
|
+
spec.add_development_dependency 'pry-nav', '~> 0.2'
|
34
|
+
spec.add_development_dependency 'pry-stack_explorer', '~> 0.4'
|
35
|
+
spec.add_development_dependency 'rb-readline', '~> 0.5'
|
36
|
+
end
|
data/lib/dropcaster/channel.rb
CHANGED
@@ -1,159 +1,195 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'uri'
|
3
|
-
|
4
|
-
module Dropcaster
|
5
|
-
#
|
6
|
-
# Represents a podcast feed in the RSS 2.0 format
|
7
|
-
#
|
8
|
-
class Channel
|
9
|
-
include ERB::Util # for h() in the ERB template
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# Instantiate a new Channel object. +sources+ must be present and can be a String or Array
|
15
|
-
# of Strings, pointing to a one or more directories or MP3 files.
|
16
|
-
#
|
17
|
-
# +attributes+ is a hash with all attributes for the channel. The following attributes are
|
18
|
-
# mandatory when a new channel is created:
|
19
|
-
#
|
20
|
-
# * <tt>:title</tt> - Title (name) of the podcast
|
21
|
-
# * <tt>:url</tt> - URL to the podcast
|
22
|
-
# * <tt>:description</tt> - Short description of the podcast (a few words)
|
23
|
-
#
|
24
|
-
def initialize(sources, attributes)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
1
|
+
require 'erb'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Dropcaster
|
5
|
+
#
|
6
|
+
# Represents a podcast feed in the RSS 2.0 format
|
7
|
+
#
|
8
|
+
class Channel
|
9
|
+
include ERB::Util # for h() in the ERB template
|
10
|
+
|
11
|
+
STORAGE_UNITS = %w(Byte KB MB GB TB)
|
12
|
+
MAX_KEYWORD_COUNT = 12
|
13
|
+
|
14
|
+
# Instantiate a new Channel object. +sources+ must be present and can be a String or Array
|
15
|
+
# of Strings, pointing to a one or more directories or MP3 files.
|
16
|
+
#
|
17
|
+
# +attributes+ is a hash with all attributes for the channel. The following attributes are
|
18
|
+
# mandatory when a new channel is created:
|
19
|
+
#
|
20
|
+
# * <tt>:title</tt> - Title (name) of the podcast
|
21
|
+
# * <tt>:url</tt> - URL to the podcast
|
22
|
+
# * <tt>:description</tt> - Short description of the podcast (a few words)
|
23
|
+
#
|
24
|
+
def initialize(sources, attributes)
|
25
|
+
# Assert mandatory attributes
|
26
|
+
[:title, :url, :description].each{|attr|
|
27
|
+
raise MissingAttributeError.new(attr) if attributes[attr].blank?
|
28
|
+
}
|
29
|
+
|
30
|
+
@attributes = attributes
|
31
|
+
@attributes[:explicit] = yes_no_or_input(attributes[:explicit])
|
32
|
+
@source_files = Array.new
|
33
|
+
|
34
|
+
# if (sources.respond_to?(:each)) # array
|
35
|
+
if sources.is_a? Array
|
36
|
+
sources.each do |src|
|
37
|
+
add_files(src)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
# single file or directory
|
41
|
+
add_files(sources)
|
42
|
+
end
|
43
|
+
|
44
|
+
# If not absolute, prepend the image URL with the channel's base to make an absolute URL
|
45
|
+
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]}'")
|
47
|
+
@attributes[:image_url] = (URI.parse(@attributes[:url]) + @attributes[:image_url]).to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
# If enclosures_url is not given, take the channel URL as a base.
|
51
|
+
if @attributes[:enclosures_url].blank?
|
52
|
+
Dropcaster.logger.info("No enclosure URL given, using the channel's enclosure URL")
|
53
|
+
@attributes[:enclosures_url] = @attributes[:url]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Warn if keyword count is larger than recommended
|
57
|
+
assert_keyword_count(@attributes[:keywords])
|
58
|
+
|
59
|
+
channel_template = @attributes[:channel_template] || File.join(File.dirname(__FILE__), '..', '..', 'templates', 'channel.rss.erb')
|
60
|
+
|
61
|
+
begin
|
62
|
+
@erb_template = ERB.new(IO.read(channel_template), 0, "%<>")
|
63
|
+
rescue Errno::ENOENT => e
|
64
|
+
raise TemplateNotFoundError.new(e.message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Returns this channel as an RSS representation. The actual rendering is done with the help
|
70
|
+
# of an ERB template. By default, it is expected as ../../templates/channel.rss.erb (relative)
|
71
|
+
# to channel.rb.
|
72
|
+
#
|
73
|
+
def to_rss
|
74
|
+
@erb_template.result(binding)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Returns all items (episodes) of this channel, ordered by newest-first.
|
79
|
+
#
|
80
|
+
def items
|
81
|
+
all_items = Array.new
|
82
|
+
@source_files.each{|src|
|
83
|
+
|
84
|
+
item = Item.new(src)
|
85
|
+
|
86
|
+
Dropcaster.logger.debug("Adding new item from file #{src}")
|
87
|
+
|
88
|
+
# set author and image_url from channel if empty
|
89
|
+
if item.artist.blank?
|
90
|
+
Dropcaster.logger.info("#{src} has no artist, using the channel's author")
|
91
|
+
item.tag.artist = @attributes[:author]
|
92
|
+
end
|
93
|
+
|
94
|
+
if item.image_url.blank?
|
95
|
+
Dropcaster.logger.info("#{src} has no image URL set, using the channel's image URL")
|
96
|
+
item.image_url = @attributes[:image_url]
|
97
|
+
end
|
98
|
+
|
99
|
+
# construct absolute URL, based on the channel's enclosures_url attribute
|
100
|
+
item.url = (URI.parse(enclosures_url) + item.file_name)
|
101
|
+
|
102
|
+
# Warn if keyword count is larger than recommended
|
103
|
+
assert_keyword_count(item.keywords)
|
104
|
+
|
105
|
+
all_items << item
|
106
|
+
}
|
107
|
+
|
108
|
+
all_items.sort{|x, y| y.pub_date <=> x.pub_date}
|
109
|
+
end
|
110
|
+
|
111
|
+
# from http://stackoverflow.com/questions/4136248
|
112
|
+
def humanize_time(secs)
|
113
|
+
[[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
|
114
|
+
if secs > 0
|
115
|
+
secs, n = secs.divmod(count)
|
116
|
+
"#{n.to_i}#{name}"
|
117
|
+
end
|
118
|
+
}.compact.reverse.join(' ')
|
119
|
+
end
|
120
|
+
|
121
|
+
# Fixed version of https://gist.github.com/260184
|
122
|
+
def humanize_size(number)
|
123
|
+
return nil if number.nil?
|
124
|
+
|
125
|
+
storage_units_format = '%n %u'
|
126
|
+
|
127
|
+
if number.to_i < 1024
|
128
|
+
unit = number > 1 ? 'Bytes' : 'Byte'
|
129
|
+
return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
|
130
|
+
else
|
131
|
+
max_exp = STORAGE_UNITS.size - 1
|
132
|
+
number = Float(number)
|
133
|
+
exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
|
134
|
+
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
|
135
|
+
number /= 1024 ** exponent
|
136
|
+
|
137
|
+
unit = STORAGE_UNITS[exponent]
|
138
|
+
return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# delegate all unknown methods to @attributes
|
143
|
+
def method_missing(meth, *args)
|
144
|
+
m = meth.id2name
|
145
|
+
if /=$/ =~ m
|
146
|
+
@attributes[m.chop.to_sym] = (args.length < 2 ? args[0] : args)
|
147
|
+
else
|
148
|
+
@attributes[m.to_sym]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
def add_files(src)
|
154
|
+
if File.directory?(src)
|
155
|
+
@source_files.concat(Dir.glob(File.join(src, '*.mp3')))
|
156
|
+
else
|
157
|
+
@source_files << src
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Deal with Ruby's autoboxing of Yes, No, true, etc values in YAML
|
163
|
+
#
|
164
|
+
def yes_no_or_input(flag)
|
165
|
+
case flag
|
166
|
+
when nil
|
167
|
+
nil
|
168
|
+
when true
|
169
|
+
'Yes'
|
170
|
+
when false
|
171
|
+
'No'
|
172
|
+
else
|
173
|
+
flag
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
# http://snippets.dzone.com/posts/show/4578
|
179
|
+
#
|
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
|
+
end
|
190
|
+
|
191
|
+
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
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -1,57 +1,57 @@
|
|
1
|
-
module Dropcaster
|
2
|
-
#
|
3
|
-
# Encapsulates the strategy how to find the channel definition file
|
4
|
-
#
|
5
|
-
class ChannelFileLocator
|
6
|
-
class << self
|
7
|
-
#
|
8
|
-
# Locates the channel definition file based on the <tt>sources</tt> directory.
|
9
|
-
#
|
10
|
-
# * If <tt>sources</tt> is a single file name, the channel definition file is expected
|
11
|
-
# as channel.yml in the same directory.
|
12
|
-
#
|
13
|
-
# * If <tt>sources</tt> is a single directory name, the channel definition file is
|
14
|
-
# expected as channel.yml in that directory.
|
15
|
-
#
|
16
|
-
# * If <tt>sources</tt> is an array of file names, the channel definition file is
|
17
|
-
# expected as channel.yml in the directory common to all files. If the files are
|
18
|
-
# located in more than one directory, an AmbiguousSourcesError is raised. In that
|
19
|
-
# case, the caller should specify the channel.yml as command line parameter.
|
20
|
-
#
|
21
|
-
# * If <tt>sources</tt> is an array with more than a single directory name, an
|
22
|
-
# AmbiguousSourcesError is raised. In that case, the caller should specify the
|
23
|
-
# channel.yml as command line parameter.
|
24
|
-
#
|
25
|
-
def locate(sources)
|
26
|
-
channel_source_dir = nil
|
27
|
-
|
28
|
-
if sources.respond_to?(:at)
|
29
|
-
# 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
|
31
|
-
|
32
|
-
if 1 == distinct_dirs.size
|
33
|
-
# If all are the in same directory, use that as source directory where channel.yml is expected.
|
34
|
-
channel_source_dir = distinct_dirs.first
|
35
|
-
else
|
36
|
-
# Since no channel_file was specified at the command line, throw and quit
|
37
|
-
raise AmbiguousSourcesError.new(sources)
|
38
|
-
end
|
39
|
-
else
|
40
|
-
# If a single file or directory is given, use that as source directory where channel.yml is expected.
|
41
|
-
channel_source_dir = dir_or_self(sources)
|
42
|
-
end
|
43
|
-
|
44
|
-
File.join(channel_source_dir, CHANNEL_YML)
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
def dir_or_self(source)
|
49
|
-
if File.directory?(source)
|
50
|
-
source
|
51
|
-
else
|
52
|
-
File.dirname(source)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
1
|
+
module Dropcaster
|
2
|
+
#
|
3
|
+
# Encapsulates the strategy how to find the channel definition file
|
4
|
+
#
|
5
|
+
class ChannelFileLocator
|
6
|
+
class << self
|
7
|
+
#
|
8
|
+
# Locates the channel definition file based on the <tt>sources</tt> directory.
|
9
|
+
#
|
10
|
+
# * If <tt>sources</tt> is a single file name, the channel definition file is expected
|
11
|
+
# as channel.yml in the same directory.
|
12
|
+
#
|
13
|
+
# * If <tt>sources</tt> is a single directory name, the channel definition file is
|
14
|
+
# expected as channel.yml in that directory.
|
15
|
+
#
|
16
|
+
# * If <tt>sources</tt> is an array of file names, the channel definition file is
|
17
|
+
# expected as channel.yml in the directory common to all files. If the files are
|
18
|
+
# located in more than one directory, an AmbiguousSourcesError is raised. In that
|
19
|
+
# case, the caller should specify the channel.yml as command line parameter.
|
20
|
+
#
|
21
|
+
# * If <tt>sources</tt> is an array with more than a single directory name, an
|
22
|
+
# AmbiguousSourcesError is raised. In that case, the caller should specify the
|
23
|
+
# channel.yml as command line parameter.
|
24
|
+
#
|
25
|
+
def locate(sources)
|
26
|
+
channel_source_dir = nil
|
27
|
+
|
28
|
+
if sources.respond_to?(:at)
|
29
|
+
# 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
|
31
|
+
|
32
|
+
if 1 == distinct_dirs.size
|
33
|
+
# If all are the in same directory, use that as source directory where channel.yml is expected.
|
34
|
+
channel_source_dir = distinct_dirs.first
|
35
|
+
else
|
36
|
+
# Since no channel_file was specified at the command line, throw and quit
|
37
|
+
raise AmbiguousSourcesError.new(sources)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
# If a single file or directory is given, use that as source directory where channel.yml is expected.
|
41
|
+
channel_source_dir = dir_or_self(sources)
|
42
|
+
end
|
43
|
+
|
44
|
+
File.join(channel_source_dir, CHANNEL_YML)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def dir_or_self(source)
|
49
|
+
if File.directory?(source)
|
50
|
+
source
|
51
|
+
else
|
52
|
+
File.dirname(source)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/dropcaster/errors.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
module Dropcaster
|
2
|
-
class ConfigurationError < StandardError
|
3
|
-
def initialize(msg)
|
4
|
-
super(msg)
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
class MissingAttributeError < ConfigurationError
|
9
|
-
def initialize(missingAttribute)
|
10
|
-
super("#{missingAttribute} is a mandatory channel attribute, but it is missing.")
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class AmbiguousSourcesError < ConfigurationError
|
15
|
-
def initialize(ambiguousSources)
|
16
|
-
super("The list of sources is ambiguous. Can't derive common directory from these: #{ambiguousSources.inspect}")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class TemplateNotFoundError < ConfigurationError
|
21
|
-
def initialize(message)
|
22
|
-
super("Unable to load template file: #{message}")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
module Dropcaster
|
2
|
+
class ConfigurationError < StandardError
|
3
|
+
def initialize(msg)
|
4
|
+
super(msg)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class MissingAttributeError < ConfigurationError
|
9
|
+
def initialize(missingAttribute)
|
10
|
+
super("#{missingAttribute} is a mandatory channel attribute, but it is missing.")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class AmbiguousSourcesError < ConfigurationError
|
15
|
+
def initialize(ambiguousSources)
|
16
|
+
super("The list of sources is ambiguous. Can't derive common directory from these: #{ambiguousSources.inspect}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TemplateNotFoundError < ConfigurationError
|
21
|
+
def initialize(message)
|
22
|
+
super("Unable to load template file: #{message}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|