das_catalog 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +65 -0
- data/bin/das_catalog_sync +24 -0
- data/lib/das_catalog/downloader.rb +18 -0
- data/lib/das_catalog/feed.rb +11 -0
- data/lib/das_catalog/screencast.rb +30 -0
- data/lib/das_catalog/screencast_data.rb +43 -0
- data/lib/das_catalog/std_logger.rb +21 -0
- data/lib/das_catalog/store.rb +19 -0
- data/lib/das_catalog/version.rb +3 -0
- data/lib/das_catalog.rb +55 -0
- data/spec/das_catalog_spec.rb +7 -0
- data/spec/das_downloader_spec.rb +6 -0
- data/spec/das_feed_spec.rb +8 -0
- data/spec/das_screencast_data_spec.rb +52 -0
- data/spec/das_screencast_spec.rb +77 -0
- data/spec/das_std_logger_spec.rb +35 -0
- data/spec/das_store_spec.rb +21 -0
- data/spec/spec_helper.rb +22 -0
- metadata +139 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Brendon Murphy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
Das Catalog
|
2
|
+
===========
|
3
|
+
|
4
|
+
The Das Catalog gem - used for downloading screencasts from the [Destroy All Software screencasts catalog](https://www.destroyallsoftware.com/screencasts). Uses the rss feed plus Mechanize to log you in and then download unsaved movies to a local directory.
|
5
|
+
|
6
|
+
This gem is in not linked to Destroy All Software or endorsed by Gary Bernhardt. I just like his stuff.
|
7
|
+
|
8
|
+
Requirements
|
9
|
+
------------
|
10
|
+
|
11
|
+
I've written and only run the gem on Ruby 1.9.2. YMMV, but 1.8.x will definitely blow up.
|
12
|
+
|
13
|
+
The gem also expects that wget exists in your path, as it uses it for downloaded the movie file.
|
14
|
+
|
15
|
+
Installation
|
16
|
+
------------
|
17
|
+
|
18
|
+
Install the gem
|
19
|
+
|
20
|
+
$ gem install das_catalog
|
21
|
+
|
22
|
+
Usage
|
23
|
+
-----
|
24
|
+
|
25
|
+
Run from the command line:
|
26
|
+
|
27
|
+
$ das_catalog_sync
|
28
|
+
|
29
|
+
It will prompt your for some settings (username, password, storage directory) and then be off and running.
|
30
|
+
|
31
|
+
Stored Configuration
|
32
|
+
--------------------
|
33
|
+
|
34
|
+
If you don't provide configuration, `das_catalog_sync` will prompt you for configuration values when you run it.
|
35
|
+
|
36
|
+
However, you can persist a full or partial config in ~/.das_catalog.yml as such:
|
37
|
+
|
38
|
+
---
|
39
|
+
username: your_username
|
40
|
+
password: your_password
|
41
|
+
downloads_directory: /Users/programmer/Movies/DAS
|
42
|
+
|
43
|
+
If you wish to exclude any config, leave it out and `das_catalog_sync` will prompt your for it at runtime.
|
44
|
+
|
45
|
+
Basic Process
|
46
|
+
-------------
|
47
|
+
|
48
|
+
The steps by which the gem works are fairly simple:
|
49
|
+
|
50
|
+
1. Fetch the rss feed to see what is in the catalog
|
51
|
+
2. Use Mechanize to log in at the DAS sign in page
|
52
|
+
3. Find links in the feed that have not been downloaded yet
|
53
|
+
4. Construct a download link, and grab the location header from the redirect (the movie on S3)
|
54
|
+
5. Offload the download to wget for efficient processing
|
55
|
+
|
56
|
+
TODO
|
57
|
+
----
|
58
|
+
* Finish missing spec coverage (mostly in DasCatalog toplevel namespace)
|
59
|
+
* Hook up Aruba for bin testing
|
60
|
+
* Switch bin to [GLI](https://github.com/davetron5000/gli)?
|
61
|
+
* CLI interface for tracking if you've watched a specific screencast
|
62
|
+
* Make missing directory automatically
|
63
|
+
* better wget detection/warnings
|
64
|
+
* Lighten some dependencies (feed fetching, highline, etc)
|
65
|
+
* Better cli access to modify the underlying pstore
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'highline/import'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'das_catalog'
|
9
|
+
|
10
|
+
config_file = File.expand_path("~/.das_catalog.yml")
|
11
|
+
|
12
|
+
config = if File.exists?(config_file)
|
13
|
+
YAML.load_file(config_file)
|
14
|
+
else
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
DasCatalog.configure do |c|
|
19
|
+
c.username = config["username"] || ask("Enter your DAS username: ") { |q| q.echo = true }
|
20
|
+
c.password = config["password"] || ask("Enter your DAS password: ") { |q| q.echo = "*" }
|
21
|
+
c.downloads_directory = config["downloads_directory"] || ask("Enter your DAS downloads directory: ") { |q| q.echo = "*" }
|
22
|
+
end
|
23
|
+
|
24
|
+
DasCatalog.sync
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module DasCatalog
|
4
|
+
# TODO spec this
|
5
|
+
class Downloader
|
6
|
+
def self.process(screencast)
|
7
|
+
# Mechanize was choking on large file downloads and keeps them in memory,
|
8
|
+
# outsource to wget
|
9
|
+
agent = DasCatalog.agent
|
10
|
+
agent.redirect_ok = false
|
11
|
+
result = agent.get screencast.download_link
|
12
|
+
url = result.header["location"]
|
13
|
+
url =~ /^http.+?([^\/]+\.mov)\?/
|
14
|
+
output_filename = $1
|
15
|
+
`wget --quiet #{Shellwords.escape url} -O #{DasCatalog.downloads_directory}/#{output_filename}`
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DasCatalog
|
2
|
+
class Screencast
|
3
|
+
extend Forwardable
|
4
|
+
include LoggerAccess
|
5
|
+
|
6
|
+
attr_reader :screencast_data
|
7
|
+
def_delegators :@screencast_data, :watched?, :downloaded?, :download_link
|
8
|
+
|
9
|
+
def initialize(screencast_data)
|
10
|
+
@screencast_data = screencast_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.for_link(link)
|
14
|
+
new ScreencastData.for_link(link)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
screencast_data.link
|
19
|
+
end
|
20
|
+
|
21
|
+
def download
|
22
|
+
return false if screencast_data.downloaded?
|
23
|
+
log.info "Starting download of #{self}"
|
24
|
+
Downloader.process(self)
|
25
|
+
screencast_data.downloaded
|
26
|
+
log.info "Finished download of #{self}"
|
27
|
+
screencast_data.save
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module DasCatalog
|
2
|
+
class ScreencastData
|
3
|
+
attr_reader :link, :watched, :downloaded
|
4
|
+
|
5
|
+
def initialize(link)
|
6
|
+
@link = link
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.for_link(link)
|
10
|
+
Store.find(link) or new(link)
|
11
|
+
end
|
12
|
+
|
13
|
+
alias :id :link
|
14
|
+
|
15
|
+
def download_link
|
16
|
+
"#{link}/download"
|
17
|
+
end
|
18
|
+
|
19
|
+
def downloaded
|
20
|
+
@downloaded = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def downloaded?
|
24
|
+
@downloaded
|
25
|
+
end
|
26
|
+
|
27
|
+
def watched
|
28
|
+
@watched = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def unwatched
|
32
|
+
@watched = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def watched?
|
36
|
+
@watched
|
37
|
+
end
|
38
|
+
|
39
|
+
def save
|
40
|
+
Store.store(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module DasCatalog
|
5
|
+
class StdLogger < DelegateClass(::Logger)
|
6
|
+
def initialize
|
7
|
+
@logger = ::Logger.new(STDOUT)
|
8
|
+
super(@logger)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module LoggerAccess
|
13
|
+
def self.included(klass)
|
14
|
+
klass.extend(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def log
|
18
|
+
DasCatalog.logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
module DasCatalog
|
4
|
+
class Store
|
5
|
+
def self.find(id)
|
6
|
+
store = PStore.new(DasCatalog.tracker_file)
|
7
|
+
store.transaction(true) do
|
8
|
+
store[id]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.store(screencast_data)
|
13
|
+
store = PStore.new(DasCatalog.tracker_file)
|
14
|
+
store.transaction do
|
15
|
+
store[screencast_data.id] = screencast_data
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/das_catalog.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
|
3
|
+
module DasCatalog
|
4
|
+
SIGN_IN_URL = "https://www.destroyallsoftware.com/screencasts/users/sign_in"
|
5
|
+
|
6
|
+
# TODO spec this
|
7
|
+
class << self
|
8
|
+
attr_accessor :username
|
9
|
+
attr_accessor :password
|
10
|
+
attr_accessor :downloads_directory
|
11
|
+
|
12
|
+
attr_writer :logger
|
13
|
+
def logger
|
14
|
+
@logger ||= StdLogger.new
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_writer :tracker_file
|
18
|
+
def tracker_file
|
19
|
+
@tracker_file ||= File.expand_path("~/.das_tracker.pstore")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configure
|
24
|
+
yield self
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO spec this
|
28
|
+
def self.agent
|
29
|
+
@agent ||= begin
|
30
|
+
agent = Mechanize.new
|
31
|
+
page = agent.get SIGN_IN_URL
|
32
|
+
form = page.forms.first
|
33
|
+
form['user[email]'] = username
|
34
|
+
form['user[password]'] = password
|
35
|
+
agent.submit form
|
36
|
+
agent
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO spec this
|
41
|
+
def self.sync
|
42
|
+
feed = Feed.get
|
43
|
+
feed.entries.each do |entry|
|
44
|
+
sc = Screencast.for_link(entry.url)
|
45
|
+
sc.download
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
require_relative "das_catalog/downloader"
|
51
|
+
require_relative "das_catalog/feed"
|
52
|
+
require_relative "das_catalog/std_logger"
|
53
|
+
require_relative "das_catalog/screencast"
|
54
|
+
require_relative "das_catalog/screencast_data"
|
55
|
+
require_relative "das_catalog/store"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe DasCatalog::ScreencastData do
|
4
|
+
let(:link) { "https://www.destroyallsoftware.com/screencasts/catalog/statistics-over-git-repositories" }
|
5
|
+
subject { DasCatalog::ScreencastData.new(link) }
|
6
|
+
|
7
|
+
it "creates a download link by appending download to the link" do
|
8
|
+
subject.download_link.must_equal("#{link}/download")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "uses the link as the id" do
|
12
|
+
subject.id.must_equal link
|
13
|
+
end
|
14
|
+
|
15
|
+
it "doesn't report as downloaded if it hasn't been" do
|
16
|
+
refute subject.downloaded?
|
17
|
+
end
|
18
|
+
|
19
|
+
it "reports as downloaded if downloaded" do
|
20
|
+
subject.downloaded
|
21
|
+
assert subject.downloaded?
|
22
|
+
end
|
23
|
+
|
24
|
+
it "reports as watched if watched" do
|
25
|
+
subject.watched
|
26
|
+
assert subject.watched?
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can be unwatched" do
|
30
|
+
subject.unwatched
|
31
|
+
refute subject.watched?
|
32
|
+
end
|
33
|
+
|
34
|
+
it "saves by passing itself to the tracker" do
|
35
|
+
DasCatalog::Store.expects(:store).with(subject)
|
36
|
+
subject.save
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "getting the screencast data for a given link" do
|
40
|
+
it "creates a new screencast data if one doeesn't already exist" do
|
41
|
+
skip "needs new_record?"
|
42
|
+
scd = DasCatalog::ScreencastData.for_link(link)
|
43
|
+
assert scd.new_record?
|
44
|
+
end
|
45
|
+
|
46
|
+
it "finds the screencast data if already persisted" do
|
47
|
+
DasCatalog::Store.expects(:find).with(link).returns(subject)
|
48
|
+
DasCatalog::ScreencastData.for_link(link).must_equal(subject)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe DasCatalog::Screencast do
|
4
|
+
let(:link) { "https://www.destroyallsoftware.com/screencasts/catalog/statistics-over-git-repositories" }
|
5
|
+
let(:screencast_data) { sd = mock("ScreencastData"); sd.stubs(:link).returns(link); sd }
|
6
|
+
subject { DasCatalog::Screencast.new(screencast_data) }
|
7
|
+
|
8
|
+
it "is created from screencast data" do
|
9
|
+
screencast = DasCatalog::Screencast.new(screencast_data)
|
10
|
+
screencast.screencast_data.must_equal(screencast_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "contains screencast data" do
|
14
|
+
assert subject.screencast_data
|
15
|
+
end
|
16
|
+
|
17
|
+
it "is converting to a string that is the link" do
|
18
|
+
subject.to_s.must_equal link
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can be created for a given link" do
|
22
|
+
screencast_data = mock()
|
23
|
+
DasCatalog::ScreencastData.expects(:for_link).with(link).returns(screencast_data)
|
24
|
+
DasCatalog::Screencast.for_link(link).screencast_data.must_equal screencast_data
|
25
|
+
end
|
26
|
+
|
27
|
+
it "delegates to the screencast data for watched?" do
|
28
|
+
screencast_data.stubs(:watched?).returns(false, true)
|
29
|
+
refute subject.watched?
|
30
|
+
assert subject.watched?
|
31
|
+
end
|
32
|
+
|
33
|
+
it "delegates to the screencast data for downloaded?" do
|
34
|
+
screencast_data.stubs(:downloaded?).returns(false, true)
|
35
|
+
refute subject.downloaded?
|
36
|
+
assert subject.downloaded?
|
37
|
+
end
|
38
|
+
|
39
|
+
it "delegates to the screencast data for download link" do
|
40
|
+
screencast_data.stubs(:download_link).returns("http://example.com/path")
|
41
|
+
subject.download_link.must_equal "http://example.com/path"
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "downloading" do
|
45
|
+
before do
|
46
|
+
DasCatalog::Downloader.stubs(:process)
|
47
|
+
screencast_data.stubs(:save)
|
48
|
+
screencast_data.stubs(:downloaded?)
|
49
|
+
screencast_data.stubs(:downloaded)
|
50
|
+
end
|
51
|
+
|
52
|
+
context "for a screencast that hasn't been downloaded" do
|
53
|
+
it "occurs by sending itself to the downloader for processing" do
|
54
|
+
DasCatalog::Downloader.expects(:process).with(subject)
|
55
|
+
subject.download
|
56
|
+
end
|
57
|
+
|
58
|
+
it "marks the screencast data downloaded" do
|
59
|
+
screencast_data.expects(:downloaded)
|
60
|
+
subject.download
|
61
|
+
end
|
62
|
+
|
63
|
+
it "saves the screencast data" do
|
64
|
+
screencast_data.expects(:save)
|
65
|
+
subject.download
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "for a screencast that hasn't been downloaded" do
|
70
|
+
it "returns false" do
|
71
|
+
screencast_data.expects(:downloaded?).returns(true)
|
72
|
+
refute subject.download
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe DasCatalog::StdLogger do
|
4
|
+
it "creates a standard ruby logger to STDOUT" do
|
5
|
+
::Logger.expects(:new).with(STDOUT)
|
6
|
+
DasCatalog::StdLogger.new
|
7
|
+
end
|
8
|
+
|
9
|
+
[:fatal, :error, :warn, :info, :debug].each do |log_method|
|
10
|
+
it "delegates #{log_method} to the contained logger" do
|
11
|
+
logger = DasCatalog::StdLogger.new
|
12
|
+
assert_respond_to(logger, log_method)
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "the logger mixin" do
|
18
|
+
let(:klass) { Class.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
DasCatalog.logger = mock("Logger")
|
22
|
+
klass.send(:include, DasCatalog::LoggerAccess)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "adds a log class method to grab the DasCatalog.logger" do
|
26
|
+
assert klass.respond_to?(:log)
|
27
|
+
klass.log.must_equal(DasCatalog.logger)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "also adds a log instance method" do
|
31
|
+
assert klass.new.respond_to?(:log)
|
32
|
+
klass.log.must_equal(DasCatalog.logger)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe DasCatalog::Store do
|
4
|
+
let(:link) { "https://www.destroyallsoftware.com/screencasts/catalog/statistics-over-git-repositories" }
|
5
|
+
let(:id) { link }
|
6
|
+
let(:screencast_data) { scd = mock(); scd.stubs(:id).returns(id); scd }
|
7
|
+
|
8
|
+
it "tracks in the file specified in config" do
|
9
|
+
DasCatalog::Store.store(screencast_data)
|
10
|
+
assert File.exists?(DasCatalog.tracker_file.path)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can find the screencast by the id" do
|
14
|
+
DasCatalog::Store.store(screencast_data)
|
15
|
+
assert DasCatalog::Store.find(id)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is nil if the screencast is not found" do
|
19
|
+
refute DasCatalog::Store.find(id)
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
gem "minitest"
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/pride'
|
5
|
+
require 'mocha'
|
6
|
+
require 'fakefs/safe'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'tempfile'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
require_relative '../lib/das_catalog'
|
12
|
+
|
13
|
+
alias :context :describe
|
14
|
+
|
15
|
+
null_logger = ::Logger.new("/dev/null")
|
16
|
+
|
17
|
+
MiniTest::Spec.add_setup_hook do
|
18
|
+
DasCatalog.configure do |c|
|
19
|
+
c.tracker_file = Tempfile.new("das_tracker")
|
20
|
+
c.logger = null_logger
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: das_catalog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brendon Murphy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-17 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: feedzirra
|
16
|
+
requirement: &2153475280 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2153475280
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mechanize
|
27
|
+
requirement: &2153473140 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2153473140
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: highline
|
38
|
+
requirement: &2153472680 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2153472680
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: minitest
|
49
|
+
requirement: &2153472260 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2153472260
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: mocha
|
60
|
+
requirement: &2153471700 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *2153471700
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: fakefs
|
71
|
+
requirement: &2153471140 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *2153471140
|
80
|
+
description: Download screencasts from Destroy All Software catalog. Uses the rss
|
81
|
+
feed plus Mechanize to log you in and then download new movies to your local drive.
|
82
|
+
email: xternal1+github@gmail.com
|
83
|
+
executables:
|
84
|
+
- das_catalog_sync
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- lib/das_catalog/downloader.rb
|
89
|
+
- lib/das_catalog/feed.rb
|
90
|
+
- lib/das_catalog/screencast.rb
|
91
|
+
- lib/das_catalog/screencast_data.rb
|
92
|
+
- lib/das_catalog/std_logger.rb
|
93
|
+
- lib/das_catalog/store.rb
|
94
|
+
- lib/das_catalog/version.rb
|
95
|
+
- lib/das_catalog.rb
|
96
|
+
- bin/das_catalog_sync
|
97
|
+
- README.md
|
98
|
+
- LICENSE
|
99
|
+
- spec/das_catalog_spec.rb
|
100
|
+
- spec/das_downloader_spec.rb
|
101
|
+
- spec/das_feed_spec.rb
|
102
|
+
- spec/das_screencast_data_spec.rb
|
103
|
+
- spec/das_screencast_spec.rb
|
104
|
+
- spec/das_std_logger_spec.rb
|
105
|
+
- spec/das_store_spec.rb
|
106
|
+
- spec/spec_helper.rb
|
107
|
+
homepage:
|
108
|
+
licenses: []
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 1.8.11
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: Download screencasts from Destroy All Software catalog
|
131
|
+
test_files:
|
132
|
+
- spec/das_catalog_spec.rb
|
133
|
+
- spec/das_downloader_spec.rb
|
134
|
+
- spec/das_feed_spec.rb
|
135
|
+
- spec/das_screencast_data_spec.rb
|
136
|
+
- spec/das_screencast_spec.rb
|
137
|
+
- spec/das_std_logger_spec.rb
|
138
|
+
- spec/das_store_spec.rb
|
139
|
+
- spec/spec_helper.rb
|