tapas 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +3 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +8 -0
- data/bin/tapas +104 -0
- data/lib/tapas.rb +7 -0
- data/lib/tapas/config.rb +70 -0
- data/lib/tapas/downloader.rb +60 -0
- data/lib/tapas/episode.rb +25 -0
- data/lib/tapas/episode_collection.rb +50 -0
- data/lib/tapas/episode_filter.rb +51 -0
- data/lib/tapas/exceptions.rb +3 -0
- data/lib/tapas/feed_source.rb +22 -0
- data/lib/tapas/version.rb +3 -0
- data/tapas.gemspec +32 -0
- data/test/feed.xml +107 -0
- data/test/minitest_helper.rb +6 -0
- data/test/test_config.rb +58 -0
- data/test/test_episode.rb +46 -0
- data/test/test_episode_collection.rb +33 -0
- data/test/test_episode_filter.rb +58 -0
- data/test/test_feed_source.rb +17 -0
- metadata +217 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 99329bb68ac39ab9bce498a9bac1703d5712ff20
|
4
|
+
data.tar.gz: de4f0315fa189e111dc917d0563ced75951e542a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2fb7e87468b291aacac6cded2434ad242250dd3d841440e6a0ae67ccb59603e6aa9662a2778f4d8718806157279aa5739c3f5e0d82ff0bd4a3a1627e3217c863
|
7
|
+
data.tar.gz: 32a919b80686a2255ce6f220e3f2e9c3bd352772a1185e22f9762756db0f291dc7682906fc9c17d7260d2a90be1bcde7f13bad8354b6207ef17c32b82a152688
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jon Riddle
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Tapas
|
2
|
+
|
3
|
+
Tapas downloads the latest RubyTapas episode, episodes by number, and episodes matching a given search.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install tapas
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
tapas # Download the latest episode
|
12
|
+
|
13
|
+
tapas -e 100 # Download episode 100
|
14
|
+
|
15
|
+
tapas -e 100-105 # Download episodes 100 through 105
|
16
|
+
|
17
|
+
tapas -t foo # Download episodes with title containing 'foo'
|
18
|
+
|
19
|
+
tapas -l -t foo # List (don't download) episodes with title containing 'foo'
|
20
|
+
|
21
|
+
tapas -h # Show options
|
22
|
+
|
23
|
+
|
24
|
+
## Configuration Options
|
25
|
+
|
26
|
+
The default configuration file is `~/.tapas` and is of the format:
|
27
|
+
|
28
|
+
setting: value
|
29
|
+
setting: value
|
30
|
+
|
31
|
+
The following options are supported (default value):
|
32
|
+
|
33
|
+
- username
|
34
|
+
- password
|
35
|
+
- feed_url (https://rubytapas.dpdcart.com/feed)
|
36
|
+
- feed_file # local feed - will override feed_url if set
|
37
|
+
- destination (.)
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
1. Fork it
|
42
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
43
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
44
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
45
|
+
5. Create new Pull Request
|
46
|
+
|
47
|
+
© 2013 jdrNetworking, LLC
|
data/Rakefile
ADDED
data/bin/tapas
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'tapas'
|
5
|
+
rescue NameError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'tapas'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'optparse'
|
11
|
+
|
12
|
+
options = { last: true, episodes: [] }
|
13
|
+
OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: #{File.basename($0)} [-l] [-n episode[-episode] ] [-t title] [-d description] [-s search]"
|
15
|
+
|
16
|
+
opts.separator ''
|
17
|
+
|
18
|
+
opts.on('-l', '--list-only', 'List episode(s) to be downloaded without downloading them') do
|
19
|
+
options[:list_only] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-a', '--all', 'Select all episodes') do |all|
|
23
|
+
options.delete(:last)
|
24
|
+
options[:all] = true
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-n', '--episode N[-M]', 'Select episode(s) N [through M]') do |episode|
|
28
|
+
options.delete(:last)
|
29
|
+
if episode.include?('-')
|
30
|
+
first, last = episode.split('-').map(&:to_i)
|
31
|
+
options[:episodes] += (first..last).to_a
|
32
|
+
else
|
33
|
+
options[:episodes] << episode.to_i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-t', '--title TERM', 'Select episodes with TERM in the title') do |term|
|
38
|
+
options.delete(:last)
|
39
|
+
options[:title] = term
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-d', '--description TERM', 'Select episodes with TERM in the description') do |term|
|
43
|
+
options.delete(:last)
|
44
|
+
options[:description] = term
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-s', '--search TERM', 'Select episodes with TERM in the title or description') do |term|
|
48
|
+
options.delete(:last)
|
49
|
+
options[:search] = term
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on('-q', '--quiet', 'Output nothing except errors') do |quiet|
|
53
|
+
options[:quiet] = quiet
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on('--feed-file FILE', 'Use local file instead of downloading feed') do |file|
|
57
|
+
options[:feed_file] = file
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.separator ''
|
61
|
+
opts.separator 'With no arguments, downloads the last episode'
|
62
|
+
opts.separator ''
|
63
|
+
|
64
|
+
opts.on_tail('-c', '--config', 'Show configuration options') do
|
65
|
+
options[:show_config] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
69
|
+
puts opts
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on_tail('--version') do
|
74
|
+
puts "Tapas #{Tapas::VERSION}"
|
75
|
+
exit 0
|
76
|
+
end
|
77
|
+
end.parse!
|
78
|
+
|
79
|
+
config = Tapas::Config.new(options)
|
80
|
+
|
81
|
+
if config.show_config
|
82
|
+
config.delete_setting :show_config
|
83
|
+
puts config
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
feed_source = Tapas::FeedSource.new(config)
|
89
|
+
collection = Tapas::EpisodeCollection.new(feed_source)
|
90
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
91
|
+
|
92
|
+
filter.episodes.each do |episode|
|
93
|
+
puts " #{episode}" unless config.quiet
|
94
|
+
next if config.list_only
|
95
|
+
Tapas::Downloader.new(episode, config).download
|
96
|
+
end
|
97
|
+
rescue Tapas::UnauthorizedError
|
98
|
+
puts "Uh oh! It looks like the feed requires authentication."
|
99
|
+
if config.username && config.password
|
100
|
+
puts "Your username and/or password were not accepted."
|
101
|
+
else
|
102
|
+
puts "No username or password were supplied. Set them in #{config.default_path}"
|
103
|
+
end
|
104
|
+
end
|
data/lib/tapas.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
require_relative "tapas/exceptions"
|
2
|
+
require_relative "tapas/config"
|
3
|
+
require_relative "tapas/feed_source"
|
4
|
+
require_relative "tapas/episode"
|
5
|
+
require_relative "tapas/episode_collection"
|
6
|
+
require_relative "tapas/episode_filter"
|
7
|
+
require_relative "tapas/downloader"
|
data/lib/tapas/config.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Tapas
|
5
|
+
class Config
|
6
|
+
attr_accessor :path
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
@path = ::Pathname.new(args.delete(:path) || default_path)
|
11
|
+
@config = default_config
|
12
|
+
load_config(path.expand_path)
|
13
|
+
args.each { |k,v| config[k] = v }
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_path
|
17
|
+
'~/.tapas'
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_setting(setting)
|
21
|
+
config.delete_field setting
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(name, *args)
|
25
|
+
if name.to_s.end_with?('=')
|
26
|
+
key = name.to_s.gsub(/=$/, '')
|
27
|
+
config[key] = args.first
|
28
|
+
else
|
29
|
+
config[name]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def respond_to_missing?(name)
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
max_length = config.to_h.keys.map(&:length).max
|
39
|
+
config.each_pair.map do |setting, value|
|
40
|
+
"%-#{max_length}s = %s" % format_setting_value(setting, value)
|
41
|
+
end.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def default_config
|
47
|
+
::OpenStruct.new.tap do |config|
|
48
|
+
config.feed_url = 'https://rubytapas.dpdcart.com/feed'
|
49
|
+
config.destination = '.'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_config(path)
|
54
|
+
return unless path.exist?
|
55
|
+
::YAML.load_file(path).each do |setting, value|
|
56
|
+
config[setting] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def format_setting_value(setting, value)
|
61
|
+
return [ setting, value ? '✓' : '✕' ] if [true, false].include?(value)
|
62
|
+
|
63
|
+
case setting
|
64
|
+
when :destination then [ setting, ::File.expand_path(value) ]
|
65
|
+
when :password then [ setting, value[0,1] + '*' * (value.length - 2) + value[-1,1] ]
|
66
|
+
else [ setting, value ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'ruby-progressbar'
|
3
|
+
|
4
|
+
module Tapas
|
5
|
+
class Downloader
|
6
|
+
attr_reader :episode, :config, :output, :progress_bar
|
7
|
+
|
8
|
+
def initialize(episode, config)
|
9
|
+
@episode = episode
|
10
|
+
@config = config
|
11
|
+
@output = default_output(episode, config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def download
|
15
|
+
open_opts = {
|
16
|
+
http_basic_authentication: [config.username, config.password],
|
17
|
+
content_length_proc: length_proc,
|
18
|
+
progress_proc: progress_proc
|
19
|
+
}
|
20
|
+
open(episode.url, open_opts) do |download|
|
21
|
+
File.open(output, 'wb') do |out|
|
22
|
+
out.write download.read
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def default_output(episode, config)
|
30
|
+
filename = "RubyTapas - %3d - %s%s" % [ episode.number, episode.title, File.extname(episode.url) ]
|
31
|
+
File.expand_path(File.join(config.destination, filename))
|
32
|
+
end
|
33
|
+
|
34
|
+
def length_proc
|
35
|
+
->(total) {
|
36
|
+
if total && total > 0
|
37
|
+
@progress_bar = ProgressBar.create total: total
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def progress_proc
|
43
|
+
->(size) {
|
44
|
+
update_progress(progress_bar, size)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_progress(progress_bar, size)
|
49
|
+
return unless progress_bar
|
50
|
+
|
51
|
+
progress_bar.total = nil if progress_bar.total && size > progress_bar.total
|
52
|
+
|
53
|
+
if progress_bar.total
|
54
|
+
progress_bar.progress = size
|
55
|
+
else
|
56
|
+
progress_bar.increment
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tapas
|
2
|
+
class Episode < Struct.new(:number, :title, :description, :url)
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def title_matches?(text)
|
6
|
+
title.downcase.include?(text.downcase)
|
7
|
+
end
|
8
|
+
|
9
|
+
def description_matches?(text)
|
10
|
+
description.downcase.include?(text.downcase)
|
11
|
+
end
|
12
|
+
|
13
|
+
def <=>(other)
|
14
|
+
[number.to_i, title] <=> [other.number.to_i, other.title]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
if number
|
19
|
+
"%3d - %s" % [ number, title ]
|
20
|
+
else
|
21
|
+
" %s" % title
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'htmlentities'
|
3
|
+
|
4
|
+
module Tapas
|
5
|
+
class EpisodeCollection
|
6
|
+
attr_reader :doc, :episodes
|
7
|
+
|
8
|
+
def initialize(feed_source)
|
9
|
+
@doc = Nokogiri.parse(feed_source.feed)
|
10
|
+
@episodes = parse(doc)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def parse(doc)
|
16
|
+
doc.css('item').select do |item|
|
17
|
+
episode?(item)
|
18
|
+
end.map do |item|
|
19
|
+
number = item_number(item)
|
20
|
+
title = item_title(item)
|
21
|
+
url = item_url(item)
|
22
|
+
description = item_description(item)
|
23
|
+
Episode.new(number, title, description, url)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def item_number(item)
|
28
|
+
number = item.at(:title).text[/^\d+/]
|
29
|
+
number &&= number.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def item_title(item)
|
33
|
+
title = HTMLEntities.new.decode(item.at(:title).text[/^\d*\s*:?\s*(.*)/, 1])
|
34
|
+
end
|
35
|
+
|
36
|
+
def item_url(item)
|
37
|
+
item.at(:enclosure)[:url]
|
38
|
+
end
|
39
|
+
|
40
|
+
def item_description(item)
|
41
|
+
text = item.at(:description).text
|
42
|
+
doc = Nokogiri.parse(text)
|
43
|
+
doc.at('p').text
|
44
|
+
end
|
45
|
+
|
46
|
+
def episode?(item)
|
47
|
+
!!item.at(:enclosure)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Tapas
|
2
|
+
class EpisodeFilter
|
3
|
+
attr_reader :collection, :config
|
4
|
+
|
5
|
+
def initialize(collection, config)
|
6
|
+
@collection = collection
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def episodes
|
11
|
+
return [collection.episodes.sort.last] if config.last
|
12
|
+
return collection.episodes.sort if config.all
|
13
|
+
|
14
|
+
[
|
15
|
+
by_number(config.episodes),
|
16
|
+
title(config.title),
|
17
|
+
description(config.description),
|
18
|
+
search(config.search),
|
19
|
+
].flatten.uniq.sort
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def by_number(episode_numbers)
|
25
|
+
return [] if episode_numbers.empty?
|
26
|
+
|
27
|
+
episode_numbers.map do |episode_number|
|
28
|
+
number(episode_number)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def number(number)
|
33
|
+
collection.episodes.detect { |episode| episode.number == number }
|
34
|
+
end
|
35
|
+
|
36
|
+
def title(text)
|
37
|
+
return [] unless text
|
38
|
+
collection.episodes.select { |episode| episode.title_matches?(text) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def description(text)
|
42
|
+
return [] unless text
|
43
|
+
collection.episodes.select { |episode| episode.description_matches?(text) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def search(text)
|
47
|
+
return [] unless text
|
48
|
+
[title(text), description(text)].flatten.uniq
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Tapas
|
4
|
+
class FeedSource
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def feed
|
12
|
+
if config.feed_file
|
13
|
+
File.read(config.feed_file)
|
14
|
+
elsif config.feed_url
|
15
|
+
open(config.feed_url, http_basic_authentication: [config.username, config.password]).read
|
16
|
+
end
|
17
|
+
rescue OpenURI::HTTPError => e
|
18
|
+
raise Tapas::UnauthorizedError if e.message == "401 Unauthorized"
|
19
|
+
raise
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/tapas.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tapas/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tapas"
|
8
|
+
spec.version = Tapas::VERSION
|
9
|
+
spec.authors = ["Jon Riddle"]
|
10
|
+
spec.email = ["jon@jdrnetworking.com"]
|
11
|
+
spec.description = %q{Download RubyTapas episodes}
|
12
|
+
spec.summary = %q{tapas downloads the latest RubyTapas episode or episodes matching a given search term}
|
13
|
+
spec.homepage = "https://github.com/jdrnetworking/tapas"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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_runtime_dependency "nokogiri", "~> 1.6"
|
22
|
+
spec.add_runtime_dependency "ruby-progressbar", "~> 1.2"
|
23
|
+
spec.add_runtime_dependency "htmlentities", "~> 4.3"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "activesupport", "~> 3.2.14"
|
28
|
+
spec.add_development_dependency "minitest", "4.7.5"
|
29
|
+
spec.add_development_dependency "mocha"
|
30
|
+
spec.add_development_dependency "pry"
|
31
|
+
spec.add_development_dependency "pry-debugger"
|
32
|
+
end
|
data/test/feed.xml
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
|
3
|
+
<channel>
|
4
|
+
<title>RubyTapas</title>
|
5
|
+
<link>https://rubytapas.dpdcart.com/subscriber/content</link>
|
6
|
+
<description/>
|
7
|
+
<pubDate>Thu, 17 Oct 2013 19:14:00 -0400</pubDate>
|
8
|
+
<managingEditor>contact@shiprise.net (Avdi Grimm)</managingEditor>
|
9
|
+
<language>en</language>
|
10
|
+
<copyright>Copyright 2013 RubyTapas</copyright>
|
11
|
+
<generator>getdpd.com</generator>
|
12
|
+
<itunes:summary>RubyTapas: Small plates of gourmet code.</itunes:summary>
|
13
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
14
|
+
<image>
|
15
|
+
<url>https://getdpd.com/uploads/ruby-tapas.png</url>
|
16
|
+
<title>Ruby Tapas</title>
|
17
|
+
<link>https://rubytapas.dpdcart.com/subscriber/content</link>
|
18
|
+
<width>849</width>
|
19
|
+
<height>849</height>
|
20
|
+
</image>
|
21
|
+
<item>
|
22
|
+
<title><![CDATA[005 Array Literals]]></title>
|
23
|
+
<link>https://rubytapas.dpdcart.com/subscriber/post?id=27</link>
|
24
|
+
<description><![CDATA[<div class="blog-entry">
|
25
|
+
<div class="blog-content"><p>Composing command lines with fancy array literals.</p>
|
26
|
+
</div>
|
27
|
+
<h3>Attached Files</h3>
|
28
|
+
<ul>
|
29
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=38">RubyTapas005.mp4</a></li>
|
30
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=39">005-array-literals.html</a></li>
|
31
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=40">005-array-literals.rb</a></li>
|
32
|
+
</ul></div>]]></description>
|
33
|
+
<guid isPermaLink="false">dpd-fc854d43484d212cf08a36fd13e5cf8c7fa427d5</guid>
|
34
|
+
<pubDate>Wed, 03 Oct 2012 09:00:00 -0400</pubDate>
|
35
|
+
<enclosure url="https://rubytapas.dpdcart.com/feed/download/38/RubyTapas005.mp4" length="28831935" type="video/mp4"/>
|
36
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
37
|
+
</item>
|
38
|
+
<item>
|
39
|
+
<title><![CDATA[004 Barewords]]></title>
|
40
|
+
<link>https://rubytapas.dpdcart.com/subscriber/post?id=26</link>
|
41
|
+
<description><![CDATA[<div class="blog-entry">
|
42
|
+
<div class="blog-content"><p>In this longer-than-usual episode, some thoughts on how to enable method logic to remain stable and unchanged while evolving and changing the source and scope of the values used by the logic.</p>
|
43
|
+
</div>
|
44
|
+
<h3>Attached Files</h3>
|
45
|
+
<ul>
|
46
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=43">barewords.rb</a></li>
|
47
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=44">barewords.html</a></li>
|
48
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=45">RubyTapas-Barewords.mp4</a></li>
|
49
|
+
</ul></div>]]></description>
|
50
|
+
<guid isPermaLink="false">dpd-79af0936dda4a452fa19be8b54aecd2cb1885e2f</guid>
|
51
|
+
<pubDate>Mon, 01 Oct 2012 09:00:00 -0400</pubDate>
|
52
|
+
<enclosure url="https://rubytapas.dpdcart.com/feed/download/45/RubyTapas-Barewords.mp4" length="106907561" type="video/mp4"/>
|
53
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
54
|
+
</item>
|
55
|
+
<item>
|
56
|
+
<title><![CDATA[003 Character Literals]]></title>
|
57
|
+
<link>https://rubytapas.dpdcart.com/subscriber/post?id=21</link>
|
58
|
+
<description><![CDATA[<div class="blog-entry">
|
59
|
+
<div class="blog-content"><p>Character literal syntax in Ruby.</p>
|
60
|
+
</div>
|
61
|
+
<h3>Attached Files</h3>
|
62
|
+
<ul>
|
63
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=32">RubyTapas003.mp4</a></li>
|
64
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=33">003-char-literals.html</a></li>
|
65
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=34">003-char-literals.rb</a></li>
|
66
|
+
</ul></div>]]></description>
|
67
|
+
<guid isPermaLink="false">dpd-ed1f01e1872210288982a61742c81df4c7905a4b</guid>
|
68
|
+
<pubDate>Fri, 28 Sep 2012 09:00:00 -0400</pubDate>
|
69
|
+
<enclosure url="https://rubytapas.dpdcart.com/feed/download/32/RubyTapas003.mp4" length="6910954" type="video/mp4"/>
|
70
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
71
|
+
</item>
|
72
|
+
<item>
|
73
|
+
<title><![CDATA[002 Large Integer<Literals]]></title>
|
74
|
+
<link>https://rubytapas.dpdcart.com/subscriber/post?id=20</link>
|
75
|
+
<description><![CDATA[<div class="blog-entry">
|
76
|
+
<div class="blog-content"><p>How to format big numbers<so that they are readable.</p>
|
77
|
+
</div>
|
78
|
+
<h3>Attached Files</h3>
|
79
|
+
<ul>
|
80
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=29">RubyTapas002.mp4</a></li>
|
81
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=30">002-large-integer-literals.html</a></li>
|
82
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=31">002-large-integer-literals.rb</a></li>
|
83
|
+
</ul></div>]]></description>
|
84
|
+
<guid isPermaLink="false">dpd-8652bee88eb6fad318e874a29a26513083d47bab</guid>
|
85
|
+
<pubDate>Wed, 26 Sep 2012 09:00:00 -0400</pubDate>
|
86
|
+
<enclosure url="https://rubytapas.dpdcart.com/feed/download/29/RubyTapas002.mp4" length="4258184" type="video/mp4"/>
|
87
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
88
|
+
</item>
|
89
|
+
<item>
|
90
|
+
<title><![CDATA[001 Binary Literals]]></title>
|
91
|
+
<link>https://rubytapas.dpdcart.com/subscriber/post?id=18</link>
|
92
|
+
<description><![CDATA[<div class="blog-entry">
|
93
|
+
<div class="blog-content"><p>In this inaugural episode, a look at a handy syntax for writing out binary numbers.</p>
|
94
|
+
</div>
|
95
|
+
<h3>Attached Files</h3>
|
96
|
+
<ul>
|
97
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=25">RubyTapas001.mp4</a></li>
|
98
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=26">001-binary-literals.html</a></li>
|
99
|
+
<li><a href="https://rubytapas.dpdcart.com/subscriber/download?file_id=27">001-binary-literals.rb</a></li>
|
100
|
+
</ul></div>]]></description>
|
101
|
+
<guid isPermaLink="false">dpd-89e8004c8242e7ad548833bef1e18a5b575c92c1</guid>
|
102
|
+
<pubDate>Mon, 24 Sep 2012 09:00:00 -0400</pubDate>
|
103
|
+
<enclosure url="https://rubytapas.dpdcart.com/feed/download/25/RubyTapas001.mp4" length="12502397" type="video/mp4"/>
|
104
|
+
<itunes:image href="https://getdpd.com/uploads/ruby-tapas.png"/>
|
105
|
+
</item>
|
106
|
+
</channel>
|
107
|
+
</rss>
|
data/test/test_config.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class TestConfig < ActiveSupport::TestCase
|
5
|
+
test 'should have default config path' do
|
6
|
+
config = Tapas::Config.new
|
7
|
+
refute_nil config.path
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'should override config path' do
|
11
|
+
config = Tapas::Config.new(path: '~/.foo')
|
12
|
+
assert_equal '~/.foo', config.path.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
test 'should load configuration from config file' do
|
16
|
+
Tempfile.open('tapas') do |tempfile|
|
17
|
+
tempfile << <<-EOT
|
18
|
+
username: foo
|
19
|
+
password: bar
|
20
|
+
EOT
|
21
|
+
tempfile.close
|
22
|
+
config = Tapas::Config.new(path: tempfile.path)
|
23
|
+
assert_equal 'foo', config.username
|
24
|
+
assert_equal 'bar', config.password
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'should override config file' do
|
29
|
+
Tempfile.open('tapas') do |tempfile|
|
30
|
+
tempfile << <<-EOT
|
31
|
+
username: foo
|
32
|
+
password: bar
|
33
|
+
EOT
|
34
|
+
tempfile.close
|
35
|
+
config = Tapas::Config.new(path: tempfile.path, username: 'baz')
|
36
|
+
assert_equal 'baz', config.username
|
37
|
+
assert_equal 'bar', config.password
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'should return nil for unknown options' do
|
42
|
+
config = Tapas::Config.new
|
43
|
+
assert_nil config.foobar
|
44
|
+
end
|
45
|
+
|
46
|
+
test 'should set option' do
|
47
|
+
config = Tapas::Config.new
|
48
|
+
config.foobar = 'baz'
|
49
|
+
assert_equal 'baz', config.foobar
|
50
|
+
end
|
51
|
+
|
52
|
+
test 'should delete option' do
|
53
|
+
config = Tapas::Config.new foo: 'bar'
|
54
|
+
assert_equal 'bar', config.foo
|
55
|
+
config.delete_setting :foo
|
56
|
+
assert_nil config.foo
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
class TestEpisode < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@episode = Tapas::Episode.new(
|
6
|
+
5,
|
7
|
+
'Array Literals',
|
8
|
+
'Composing command lines with fancy array literals.',
|
9
|
+
'https://rubytapas.dpdcart.com/feed/download/38/RubyTapas005.mp4'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
test 'should match title' do
|
14
|
+
assert @episode.title_matches?('Array')
|
15
|
+
end
|
16
|
+
|
17
|
+
test 'should match title without case sensitivity' do
|
18
|
+
assert @episode.title_matches?('array')
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'should match description' do
|
22
|
+
assert @episode.description_matches?('Composing')
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'should match description without case sensitivity' do
|
26
|
+
assert @episode.description_matches?('composing')
|
27
|
+
end
|
28
|
+
|
29
|
+
test 'should be sort by episode number first' do
|
30
|
+
episode_2 = Tapas::Episode.new(4, 'Array Literals', '', '')
|
31
|
+
sorted = [@episode, episode_2].sort
|
32
|
+
assert_equal [episode_2, @episode], sorted
|
33
|
+
end
|
34
|
+
|
35
|
+
test 'should sort by title next' do
|
36
|
+
episode_2 = Tapas::Episode.new(5, 'Binary Literals', '', '')
|
37
|
+
sorted = [episode_2, @episode].sort
|
38
|
+
assert_equal [@episode, episode_2], sorted
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'should sort without episode number' do
|
42
|
+
episode_2 = Tapas::Episode.new(nil, 'Binary Literals', '', '')
|
43
|
+
sorted = [@episode, episode_2].sort
|
44
|
+
assert_equal [episode_2, @episode], sorted
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
class TestEpisodeCollection < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@collection = Tapas::EpisodeCollection.new(feed_source)
|
6
|
+
end
|
7
|
+
|
8
|
+
test 'should generate episode list' do
|
9
|
+
assert_equal 5, @collection.episodes.size
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'should set episode attributes' do
|
13
|
+
episode = @collection.episodes.first
|
14
|
+
assert_equal 'Array Literals', episode.title
|
15
|
+
assert_equal 5, episode.number
|
16
|
+
assert_equal 'Composing command lines with fancy array literals.', episode.description
|
17
|
+
assert_equal 'https://rubytapas.dpdcart.com/feed/download/38/RubyTapas005.mp4', episode.url
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'should decode HTML entities in title' do
|
21
|
+
episode = @collection.episodes.detect { |e| e.number == 2 }
|
22
|
+
assert_equal 'Large Integer<Literals', episode.title
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'should decode HTML entities in description' do
|
26
|
+
episode = @collection.episodes.detect { |e| e.number == 2 }
|
27
|
+
assert_equal 'How to format big numbers<so that they are readable.', episode.description
|
28
|
+
end
|
29
|
+
|
30
|
+
def feed_source
|
31
|
+
stub feed: File.read(File.expand_path('../feed.xml', __FILE__))
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class TestEpisodeFilter < ActiveSupport::TestCase
|
5
|
+
test 'should find episodes by number' do
|
6
|
+
config = OpenStruct.new(episodes: [1])
|
7
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
8
|
+
assert_equal 1, filter.episodes.size
|
9
|
+
assert_equal 1, filter.episodes.first.number
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'should find episode by title' do
|
13
|
+
config = OpenStruct.new(episodes: [], title: 'Barewords')
|
14
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
15
|
+
assert_equal 1, filter.episodes.size
|
16
|
+
assert filter.episodes.first.title.include?('Barewords')
|
17
|
+
end
|
18
|
+
|
19
|
+
test 'should find episode by description' do
|
20
|
+
config = OpenStruct.new(episodes: [], description: 'readable')
|
21
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
22
|
+
assert_equal 1, filter.episodes.size
|
23
|
+
assert filter.episodes.first.description.include?('readable')
|
24
|
+
end
|
25
|
+
|
26
|
+
test 'should find episodes by title or description' do
|
27
|
+
config = OpenStruct.new(episodes: [], search: 'cha')
|
28
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
29
|
+
assert_equal 2, filter.episodes.size
|
30
|
+
assert filter.episodes.any? { |episode| episode.title.downcase.include?('cha') }
|
31
|
+
assert filter.episodes.any? { |episode| episode.description.downcase.include?('cha') }
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'should find last episode' do
|
35
|
+
config = OpenStruct.new(last: true)
|
36
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
37
|
+
assert_equal 1, filter.episodes.size
|
38
|
+
assert_equal 5, filter.episodes.first.number
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'should find all episodes' do
|
42
|
+
config = OpenStruct.new(all: true)
|
43
|
+
filter = Tapas::EpisodeFilter.new(collection, config)
|
44
|
+
assert_equal collection.episodes.size, filter.episodes.size
|
45
|
+
end
|
46
|
+
|
47
|
+
def collection
|
48
|
+
stub(episodes:
|
49
|
+
[
|
50
|
+
Tapas::Episode.new(5, 'Array Literals', 'Composing command lines with fancy array literals.', 'https://rubytapas.dpdcart.com/feed/download/38/RubyTapas005.mp4'),
|
51
|
+
Tapas::Episode.new(4, 'Barewords', 'In this longer-than-usual episode, some thoughts on how to enable method logic to remain stable and unchanged while evolving and changing the source and scope of the values used by the logic.', 'https://rubytapas.dpdcart.com/feed/download/45/RubyTapas-Barewords.mp4'),
|
52
|
+
Tapas::Episode.new(3, 'Character Literals', 'Character literal syntax in Ruby.', 'https://rubytapas.dpdcart.com/feed/download/32/RubyTapas003.mp4'),
|
53
|
+
Tapas::Episode.new(2, 'Large Integer Literals', 'How to format big numbers so that they are readable.', 'https://rubytapas.dpdcart.com/feed/download/29/RubyTapas002.mp4'),
|
54
|
+
Tapas::Episode.new(1, 'Binary Literals', 'In this inaugural episode, a look at a handy syntax for writing out binary numbers.', 'https://rubytapas.dpdcart.com/feed/download/25/RubyTapas001.mp4'),
|
55
|
+
]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
class TestFeedSource < ActiveSupport::TestCase
|
4
|
+
test 'should read local file' do
|
5
|
+
config = Struct.new(:feed_file).new('foo_file')
|
6
|
+
source = Tapas::FeedSource.new(config)
|
7
|
+
File.expects(:read).with('foo_file').returns('bar')
|
8
|
+
assert_equal 'bar', source.feed
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'should fetch authenticated feed url' do
|
12
|
+
config = Struct.new(:feed_file, :feed_url, :username, :password).new(nil, 'foo_url', 'user', 'pass')
|
13
|
+
source = Tapas::FeedSource.new(config)
|
14
|
+
source.expects(:open).with('foo_url', http_basic_authentication: ['user', 'pass']).returns(StringIO.new('baz'))
|
15
|
+
assert_equal 'baz', source.feed
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tapas
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Riddle
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-progressbar
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: htmlentities
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.2.14
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.2.14
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.7.5
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.7.5
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: mocha
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry-debugger
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: Download RubyTapas episodes
|
154
|
+
email:
|
155
|
+
- jon@jdrnetworking.com
|
156
|
+
executables:
|
157
|
+
- tapas
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- .gitignore
|
162
|
+
- .travis.yml
|
163
|
+
- Gemfile
|
164
|
+
- LICENSE.txt
|
165
|
+
- README.md
|
166
|
+
- Rakefile
|
167
|
+
- bin/tapas
|
168
|
+
- lib/tapas.rb
|
169
|
+
- lib/tapas/config.rb
|
170
|
+
- lib/tapas/downloader.rb
|
171
|
+
- lib/tapas/episode.rb
|
172
|
+
- lib/tapas/episode_collection.rb
|
173
|
+
- lib/tapas/episode_filter.rb
|
174
|
+
- lib/tapas/exceptions.rb
|
175
|
+
- lib/tapas/feed_source.rb
|
176
|
+
- lib/tapas/version.rb
|
177
|
+
- tapas.gemspec
|
178
|
+
- test/feed.xml
|
179
|
+
- test/minitest_helper.rb
|
180
|
+
- test/test_config.rb
|
181
|
+
- test/test_episode.rb
|
182
|
+
- test/test_episode_collection.rb
|
183
|
+
- test/test_episode_filter.rb
|
184
|
+
- test/test_feed_source.rb
|
185
|
+
homepage: https://github.com/jdrnetworking/tapas
|
186
|
+
licenses:
|
187
|
+
- MIT
|
188
|
+
metadata: {}
|
189
|
+
post_install_message:
|
190
|
+
rdoc_options: []
|
191
|
+
require_paths:
|
192
|
+
- lib
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - '>='
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: '0'
|
203
|
+
requirements: []
|
204
|
+
rubyforge_project:
|
205
|
+
rubygems_version: 2.1.9
|
206
|
+
signing_key:
|
207
|
+
specification_version: 4
|
208
|
+
summary: tapas downloads the latest RubyTapas episode or episodes matching a given
|
209
|
+
search term
|
210
|
+
test_files:
|
211
|
+
- test/feed.xml
|
212
|
+
- test/minitest_helper.rb
|
213
|
+
- test/test_config.rb
|
214
|
+
- test/test_episode.rb
|
215
|
+
- test/test_episode_collection.rb
|
216
|
+
- test/test_episode_filter.rb
|
217
|
+
- test/test_feed_source.rb
|