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.
data/dropcaster.gemspec CHANGED
@@ -1,97 +1,36 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = %q{dropcaster}
8
- s.version = "0.0.4"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["nerab"]
12
- s.date = %q{2011-10-24}
13
- s.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.}
14
- s.email = %q{nerab@gmx.at}
15
- s.executables = ["lstags", "dropcaster", "dropcaster", "lstags"]
16
- s.extra_rdoc_files = [
17
- "LICENSE.txt",
18
- "README.md",
19
- "TODO"
20
- ]
21
- s.files = [
22
- ".document",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE.txt",
26
- "README.md",
27
- "Rakefile",
28
- "TODO",
29
- "VERSION",
30
- "bin/dropcaster",
31
- "bin/lstags",
32
- "doc/infoPane.png",
33
- "doc/lyricsPane.png",
34
- "doc/sample-channel.yml",
35
- "doc/sample-sidecar.yml",
36
- "doc/videoPane.png",
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
@@ -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 < DelegateClass(Hash)
9
- include ERB::Util # for h() in the ERB template
10
- include HashKeys
11
-
12
- STORAGE_UNITS = %w(Byte KB MB GB TB)
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
- super(Hash.new)
26
-
27
- # Assert mandatory attributes
28
- [:title, :url, :description].each{|attr|
29
- raise MissingAttributeError.new(attr) if attributes[attr].blank?
30
- }
31
-
32
- self.merge!(attributes)
33
- self.explicit = yes_no_or_input(attributes[:explicit])
34
- @source_files = Array.new
35
-
36
- if (sources.respond_to?(:each)) # array
37
- sources.each{|src|
38
- add_files(src)
39
- }
40
- else
41
- # single file or directory
42
- add_files(src)
43
- end
44
-
45
- # Prepend the image URL with the channel's base to make an absolute URL
46
- unless self.image_url.blank? || self.image_url =~ /^https?:/
47
- Dropcaster.logger.info("Channel image URL '#{self.image_url}' is relative, so we prepend it with the channel URL '#{self.url}'")
48
- self.image_url = URI.join(self.url, self.image_url).to_s
49
- end
50
-
51
- # If enclosures_url is not given, take the channel URL as a base.
52
- if self.enclosures_url.blank?
53
- Dropcaster.logger.info("No enclosure URL given, using the channel's enclosure URL")
54
- self.enclosures_url = self.url
55
- end
56
-
57
- channel_template = self.channel_template || File.join(File.dirname(__FILE__), '..', '..', 'templates', 'channel.rss.erb')
58
-
59
- begin
60
- @erb_template = ERB.new(File.new(channel_template), 0, "%<>")
61
- rescue Errno::ENOENT => e
62
- raise TemplateNotFoundError.new(e.message)
63
- end
64
- end
65
-
66
- #
67
- # Returns this channel as an RSS representation. The actual rendering is done with the help
68
- # of an ERB template. By default, it is expected as ../../templates/channel.rss.erb (relative)
69
- # to channel.rb.
70
- #
71
- def to_rss
72
- @erb_template.result(binding)
73
- end
74
-
75
- #
76
- # Returns all items (episodes) of this channel, ordered by newest-first.
77
- #
78
- def items
79
- all_items = Array.new
80
- @source_files.each{|src|
81
- item = Item.new(src)
82
-
83
- Dropcaster.logger.debug("Adding new item from file #{src}")
84
-
85
- # set author and image_url from channel if empty
86
- if item.artist.blank?
87
- Dropcaster.logger.info("#{src} has no artist, using the channel's author")
88
- item.tag.artist = self.author
89
- end
90
-
91
- if item.image_url.blank?
92
- Dropcaster.logger.info("#{src} has no image URL set, using the channel's image URL")
93
- item.image_url = self.image_url
94
- end
95
-
96
- # construct absolute URL, based on the channel's enclosures_url attribute
97
- self.enclosures_url << '/' unless self.enclosures_url =~ /\/$/
98
- item.url = URI.join(URI.escape(self.enclosures_url), URI.escape(item.file_name))
99
-
100
- all_items << item
101
- }
102
-
103
- all_items.sort{|x, y| y.pub_date <=> x.pub_date}
104
- end
105
-
106
- # from http://stackoverflow.com/questions/4136248
107
- def humanize_time(secs)
108
- [[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
109
- if secs > 0
110
- secs, n = secs.divmod(count)
111
- "#{n.to_i}#{name}"
112
- end
113
- }.compact.reverse.join(' ')
114
- end
115
-
116
- # Fixed version of https://gist.github.com/260184
117
- def humanize_size(number)
118
- return nil if number.nil?
119
-
120
- storage_units_format = '%n %u'
121
-
122
- if number.to_i < 1024
123
- unit = number > 1 ? 'Bytes' : 'Byte'
124
- return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
125
- else
126
- max_exp = STORAGE_UNITS.size - 1
127
- number = Float(number)
128
- exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
129
- exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
130
- number /= 1024 ** exponent
131
-
132
- unit = STORAGE_UNITS[exponent]
133
- return storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
134
- end
135
- end
136
-
137
- private
138
- def add_files(src)
139
- if File.directory?(src)
140
- @source_files.concat(Dir.glob(File.join(src, '*.mp3')))
141
- else
142
- @source_files << src
143
- end
144
- end
145
-
146
- #
147
- # Deal with Ruby's autoboxing of Yes, No, true, etc values in YAML
148
- #
149
- def yes_no_or_input(flag)
150
- case flag
151
- when nil : nil
152
- when true : 'Yes'
153
- when false : 'No'
154
- else
155
- flag
156
- end
157
- end
158
- end
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
@@ -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