my_episodes 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +32 -0
- data/bin/myeps +9 -0
- data/lib/my_episodes.rb +18 -0
- data/lib/my_episodes/cli.rb +62 -0
- data/lib/my_episodes/client.rb +73 -0
- data/lib/my_episodes/dump.rb +32 -0
- data/lib/my_episodes/episode.rb +56 -0
- data/lib/my_episodes/season.rb +29 -0
- data/lib/my_episodes/show.rb +44 -0
- data/lib/my_episodes/shows.rb +27 -0
- data/lib/my_episodes/table_parser.rb +16 -0
- data/lib/my_episodes/version.rb +5 -0
- data/spec/integration/cli_integration_spec.rb +77 -0
- data/spec/integration/client_integration_spec.rb +57 -0
- data/spec/my_episodes/episode_spec.rb +55 -0
- data/spec/my_episodes/season_spec.rb +56 -0
- data/spec/my_episodes/show_spec.rb +45 -0
- data/spec/spec_helper.rb +26 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: baf01e0820221ce85012d67c4cddcb60fa91fd62
|
4
|
+
data.tar.gz: a898c7f434ce68400188daf639ddd278b4df60f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f3734bcd664a66aa9c5fd9f2c24b56735e0b2f6ed4135f7907ba3a4fa8214e19d29f3181083414f43c85aa0af4516eb40d081fe554226e1d784a79c264fae33d
|
7
|
+
data.tar.gz: 5446854bfa20da02ef4ae8576503567292c3c5f7404a11ac8fb5818cd8c52120001c6967f5e587317bfc2321fc8fe028fd040b1da945f780ad15ec5453586277
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# my episodes
|
2
|
+
|
3
|
+
[](https://travis-ci.org/mthssdrbrg/my_episodes)
|
4
|
+
[](https://coveralls.io/r/mthssdrbrg/my_episodes?branch=master)
|
5
|
+
|
6
|
+
[My Episodes](http://www.myepisodes.com) helps one keep track of one's TV shows,
|
7
|
+
but unfortunately there's no API or no built-in functionality for exporting
|
8
|
+
one's history, hence this gem.
|
9
|
+
|
10
|
+
There's a simple bin script, `myeps`, that can be used to dump your history to
|
11
|
+
standard out (and then you can pipe it to a file or whatever).
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
```
|
16
|
+
gem install my_episodes
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```
|
22
|
+
Usage: myeps [options]
|
23
|
+
-D, --dump Dump history from myepisodes.com to CSV
|
24
|
+
-u, --username=USERNAME Username for myepisodes.com
|
25
|
+
-p, --password=PASSWORD Password for myepisodes.com
|
26
|
+
-s, --separator=SEP Separator for CSV output
|
27
|
+
-h, --help You're looking at it
|
28
|
+
```
|
29
|
+
|
30
|
+
## Copyright
|
31
|
+
|
32
|
+
Released under the [MIT License](http://www.opensource.org/licenses/MIT) :: 2014 Mathias Söderberg.
|
data/bin/myeps
ADDED
data/lib/my_episodes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'mechanize'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
MyEpisodesError = Class.new(StandardError)
|
8
|
+
AuthenticationError = Class.new(MyEpisodesError)
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'my_episodes/cli'
|
12
|
+
require 'my_episodes/client'
|
13
|
+
require 'my_episodes/dump'
|
14
|
+
require 'my_episodes/episode'
|
15
|
+
require 'my_episodes/season'
|
16
|
+
require 'my_episodes/show'
|
17
|
+
require 'my_episodes/shows'
|
18
|
+
require 'my_episodes/table_parser'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
class Cli
|
8
|
+
def initialize
|
9
|
+
@options = {action: :help}
|
10
|
+
@parser = create_parser
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(argv)
|
14
|
+
@parser.parse(argv)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.run(argv, io)
|
18
|
+
cli = self.new
|
19
|
+
cli.parse(argv)
|
20
|
+
cli.run(io)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(io)
|
24
|
+
case @options[:action]
|
25
|
+
when :dump
|
26
|
+
Dump.new(client, io, @options).execute
|
27
|
+
when :help
|
28
|
+
io.puts(@parser)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def client
|
35
|
+
@client ||= Client.create(*@options.values_at(:username, :password))
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_parser
|
39
|
+
OptionParser.new do |opts|
|
40
|
+
opts.on('-D', '--dump', 'Dump history from myepisodes.com to CSV') do
|
41
|
+
@options[:action] = :dump
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on('-u', '--username=USERNAME', 'Username for myepisodes.com') do |username|
|
45
|
+
@options[:username] = username
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('-p', '--password=PASSWORD', 'Password for myepisodes.com') do |passwd|
|
49
|
+
@options[:password] = passwd
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on('-s', '--separator=SEP', 'Separator for CSV output') do |separator|
|
53
|
+
@options[:separator] = separator
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on('-h', '--help', 'You\'re looking at it') do
|
57
|
+
@options[:action] = :help
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Client
|
5
|
+
def self.create(username, password, agent=Mechanize.new)
|
6
|
+
client = self.new(agent)
|
7
|
+
client.login(username, password)
|
8
|
+
client
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(agent=Mechanize.new)
|
12
|
+
@agent = agent
|
13
|
+
@page = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def login(username, password)
|
17
|
+
unless @page
|
18
|
+
page = @agent.get(login_url)
|
19
|
+
form = page.form_with(action: 'login.php', method: 'POST')
|
20
|
+
form.username = username
|
21
|
+
form.password = password
|
22
|
+
form.u = life_wasted
|
23
|
+
@page = form.submit(form.buttons.first)
|
24
|
+
ensure_logged_in
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def shows
|
29
|
+
@shows ||= Shows.new(parser).create(shows_links).to_a
|
30
|
+
end
|
31
|
+
|
32
|
+
def logout
|
33
|
+
if @page
|
34
|
+
@page.link_with(text: /Logout/).click
|
35
|
+
@page = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def parser
|
42
|
+
@parser ||= TableParser.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def shows_links
|
46
|
+
@shows_links ||= @page.links_with(href: /views\.php\?type=epsbyshow/)
|
47
|
+
end
|
48
|
+
|
49
|
+
def login_url
|
50
|
+
@login_url ||= 'http://www.myepisodes.com/login.php'
|
51
|
+
end
|
52
|
+
|
53
|
+
def life_wasted
|
54
|
+
@life_wasted ||= 'life_wasted.php'
|
55
|
+
end
|
56
|
+
|
57
|
+
def still_on_login_page?
|
58
|
+
@page.title && @page.title.match(/: Login Page/i)
|
59
|
+
end
|
60
|
+
|
61
|
+
def login_warning?
|
62
|
+
warning_text = @page.search('#divContainer div.warning').text.strip
|
63
|
+
warning_text && warning_text.match(/wrong username.+password/i)
|
64
|
+
end
|
65
|
+
|
66
|
+
def ensure_logged_in
|
67
|
+
if still_on_login_page? && login_warning?
|
68
|
+
@page = nil
|
69
|
+
raise AuthenticationError, 'wrong username / password'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Dump
|
5
|
+
def initialize(client, output, options={})
|
6
|
+
@client = client
|
7
|
+
@output = output
|
8
|
+
@separator = options.fetch(:separator, ';')
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
@output.puts(headers.join(@separator))
|
13
|
+
@client.shows.each do |show|
|
14
|
+
show.seasons.each do |season|
|
15
|
+
season.episodes.each do |episode|
|
16
|
+
@output.puts(to_line(show, season, episode).join(@separator))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def to_line(s, se, e)
|
25
|
+
[s.name, se.name, e.number, e.name, e.acquired?, e.watched?]
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
@headers ||= %w[show season number episode_name acquired watched].freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Episode
|
5
|
+
attr_reader :air_date, :show_name, :number, :name
|
6
|
+
|
7
|
+
def initialize(season)
|
8
|
+
@season = season
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(attrs)
|
12
|
+
attrs = sanitize(attrs)
|
13
|
+
@air_date = text(attrs[AIR_DATE_FIELD])
|
14
|
+
@show_name = text(attrs[SHOW_NAME_FIELD])
|
15
|
+
@number = text(attrs[NUMBER_FIELD])
|
16
|
+
@name = text(attrs[NAME_FIELD])
|
17
|
+
@acquired = checked?(attrs[ACQUIRED_FIELD])
|
18
|
+
@watched = checked?(attrs[WATCHED_FIELD])
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def season
|
23
|
+
@season.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def acquired?
|
27
|
+
!!@acquired
|
28
|
+
end
|
29
|
+
|
30
|
+
def watched?
|
31
|
+
!!@watched
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def sanitize(attrs)
|
37
|
+
attrs.reject.with_index { |a, i| i.even? }
|
38
|
+
end
|
39
|
+
|
40
|
+
def text(a)
|
41
|
+
a.text.strip
|
42
|
+
end
|
43
|
+
|
44
|
+
def checked?(a)
|
45
|
+
a.children.any? && a.children.last.attr(CHECKED) == CHECKED
|
46
|
+
end
|
47
|
+
|
48
|
+
CHECKED = 'checked'.freeze
|
49
|
+
AIR_DATE_FIELD = 0
|
50
|
+
SHOW_NAME_FIELD = 1
|
51
|
+
NUMBER_FIELD = 2
|
52
|
+
NAME_FIELD = 3
|
53
|
+
ACQUIRED_FIELD = 4
|
54
|
+
WATCHED_FIELD = 5
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Season
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(show=nil)
|
8
|
+
@show = show
|
9
|
+
@episodes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(row)
|
13
|
+
@name = row.text.strip
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(episode)
|
18
|
+
@episodes << episode
|
19
|
+
end
|
20
|
+
|
21
|
+
def episodes
|
22
|
+
@episodes.freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def show
|
26
|
+
@show.freeze
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Show
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
@seasons = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(rows)
|
13
|
+
season = nil
|
14
|
+
rows.each do |row|
|
15
|
+
if useless?(row)
|
16
|
+
next
|
17
|
+
elsif new_season?(row)
|
18
|
+
@seasons << Season.new(self).create(row)
|
19
|
+
season = @seasons.last
|
20
|
+
else
|
21
|
+
season << Episode.new(season).create(row.children)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def seasons
|
28
|
+
@seasons.freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def useless?(row)
|
34
|
+
row.text.strip.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def new_season?(row)
|
38
|
+
row.attr(CLASS) == HEADER
|
39
|
+
end
|
40
|
+
|
41
|
+
CLASS = 'class'.freeze
|
42
|
+
HEADER = 'header'.freeze
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class Shows
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(parser)
|
8
|
+
@shows = []
|
9
|
+
@parser = parser
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(links)
|
13
|
+
links.each do |link|
|
14
|
+
name = link.text
|
15
|
+
page = link.click
|
16
|
+
rows = @parser.parse(page)
|
17
|
+
@shows << Show.new(name).create(rows)
|
18
|
+
end
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
@shows.each(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MyEpisodes
|
4
|
+
class TableParser
|
5
|
+
def initialize(options={})
|
6
|
+
@options = options
|
7
|
+
@search_string = 'table.mylist tr'.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(page)
|
11
|
+
table = page.search(@search_string)
|
12
|
+
table.shift unless @options[:keep_header]
|
13
|
+
table
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
describe Cli, vcr: {cassette_name: 'my_episodes'} do
|
8
|
+
let :io do
|
9
|
+
StringIO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '-D / --dump' do
|
13
|
+
let :argv do
|
14
|
+
%w[--dump -u demo -p demo]
|
15
|
+
end
|
16
|
+
|
17
|
+
let :output do
|
18
|
+
io.string.split("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
before do
|
22
|
+
described_class.run(argv, io)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'dumps history to $stdout' do
|
26
|
+
expect(output.grep(/Akame ga KILL!/)).to_not be_empty
|
27
|
+
expect(output.grep(/Blake's 7/)).to_not be_empty
|
28
|
+
expect(output.grep(/Doctor Who/)).to_not be_empty
|
29
|
+
expect(output.grep(/Under the Dome/)).to_not be_empty
|
30
|
+
expect(output.grep(/Wilfred \(US\)/)).to_not be_empty
|
31
|
+
expect(output.size).to eq(587)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'includes headers' do
|
35
|
+
expect(output.first).to eq('show;season;number;episode_name;acquired;watched')
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with -s / --separator' do
|
39
|
+
let :argv do
|
40
|
+
%w[--dump -u demo -p demo -s ':']
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'uses given character as separator' do
|
44
|
+
expect(output.grep(/.+:.+:.+/)).to_not be_empty
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '-h / --help' do
|
50
|
+
let :argv do
|
51
|
+
%w[--help]
|
52
|
+
end
|
53
|
+
|
54
|
+
before do
|
55
|
+
described_class.run(argv, io)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'prints usage' do
|
59
|
+
expect(io.string).to match(/^Usage: /)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'without any action' do
|
64
|
+
let :argv do
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
|
68
|
+
before do
|
69
|
+
described_class.run(argv, io)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'prints usage' do
|
73
|
+
expect(io.string).to match(/^Usage: /)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
describe Client do
|
8
|
+
let :client do
|
9
|
+
described_class.create('demo', 'demo')
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
client.logout
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#login' do
|
17
|
+
let :client do
|
18
|
+
described_class.new
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when given valid username and password', vcr: {cassette_name: 'login-fail'} do
|
22
|
+
it 'does not raise an error' do
|
23
|
+
expect { client.login('demo', 'demo') }.to_not raise_error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when given invalid username and password', vcr: {cassette_name: 'login-success'} do
|
28
|
+
it 'raises an error' do
|
29
|
+
expect { client.login('demo', 'this is wrong') }.to raise_error(AuthenticationError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#shows', vcr: {cassette_name: 'my_episodes'} do
|
35
|
+
it 'parses the expected number of shows' do
|
36
|
+
expect(client.shows.size).to eq(5)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'parses seasons as expected' do
|
40
|
+
show = client.shows.find { |s| s.name == 'Doctor Who' }
|
41
|
+
expect(show.seasons.size).to eq(22)
|
42
|
+
show.seasons.each do |season|
|
43
|
+
expect(season.name).to_not be_nil
|
44
|
+
expect(season.name).to_not be_empty
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'parses episodes as expected' do
|
49
|
+
show = client.shows.find { |s| s.name == 'Under the Dome' }
|
50
|
+
seasons = show.seasons
|
51
|
+
expect(seasons.size).to eq(2)
|
52
|
+
expect(seasons.first.episodes.size).to eq(1)
|
53
|
+
expect(seasons.last.episodes.size).to eq(11)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
describe Episode do
|
8
|
+
let :episode do
|
9
|
+
described_class.new(season)
|
10
|
+
end
|
11
|
+
|
12
|
+
let :season do
|
13
|
+
Season.new('Season 1')
|
14
|
+
end
|
15
|
+
|
16
|
+
let :attributes do
|
17
|
+
Nokogiri::HTML(File.read('spec/support/resources/episode.html'))
|
18
|
+
.children.last.children.first.children.first.children
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'belongs to a Season' do
|
22
|
+
expect(episode.season).to be_a(Season)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#create' do
|
26
|
+
before do
|
27
|
+
episode.create(attributes)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'parses air date' do
|
31
|
+
expect(episode.air_date).to eq('17-Aug-2014')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'parses show name' do
|
35
|
+
expect(episode.show_name).to eq('Akame ga KILL!')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'parses number' do
|
39
|
+
expect(episode.number).to eq('S01E07')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'parses name' do
|
43
|
+
expect(episode.name).to eq('Season 1, Episode 7')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'parses `acquired` state' do
|
47
|
+
expect(episode).to be_acquired
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'parses `watched` state' do
|
51
|
+
expect(episode).to_not be_watched
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
describe Season do
|
8
|
+
let :season do
|
9
|
+
described_class.new(show)
|
10
|
+
end
|
11
|
+
|
12
|
+
let :show do
|
13
|
+
Show.new('Show')
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#create' do
|
17
|
+
it 'sets name' do
|
18
|
+
row = double(:row, text: 'Season #1')
|
19
|
+
season.create(row)
|
20
|
+
expect(season.name).to eq('Season #1')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'strips whitespace characters' do
|
24
|
+
row = double(:row, text: " Season #1\t\t\n")
|
25
|
+
season.create(row)
|
26
|
+
expect(season.name).to eq('Season #1')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns self' do
|
30
|
+
expect(season.create(double(:row, text: 'Season #1'))).to eq(season)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#show' do
|
35
|
+
it 'returns given show, but frozen' do
|
36
|
+
expect(season.show).to eq(show)
|
37
|
+
expect(season.show).to be_frozen
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#<<' do
|
42
|
+
it 'adds an Episode to list of episodes' do
|
43
|
+
episode = Episode.new(season)
|
44
|
+
season << episode
|
45
|
+
expect(season.episodes.size).to eq(1)
|
46
|
+
expect(season.episodes).to eq([episode])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#episodes' do
|
51
|
+
it 'returns a frozen list of episodes' do
|
52
|
+
expect(season.episodes).to be_frozen
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module MyEpisodes
|
7
|
+
describe Show do
|
8
|
+
let :show do
|
9
|
+
described_class.new('Show')
|
10
|
+
end
|
11
|
+
|
12
|
+
let :rows do
|
13
|
+
Nokogiri::HTML(File.read('spec/support/resources/show.html'))
|
14
|
+
.children.last.children.first.children
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#name' do
|
18
|
+
it 'returns the name of the show' do
|
19
|
+
expect(show.name).to eq('Show')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#create' do
|
24
|
+
before do
|
25
|
+
show.create(rows)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'parses consecutive seasons' do
|
29
|
+
expect(show.seasons.size).to eq(2)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses episodes correctly' do
|
33
|
+
seasons = show.seasons
|
34
|
+
expect(seasons.first.episodes.size).to eq(3)
|
35
|
+
expect(seasons.last.episodes.size).to eq(1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#seasons' do
|
40
|
+
it 'returns a frozen list of seasons' do
|
41
|
+
expect(show.seasons).to be_frozen
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'vcr'
|
4
|
+
require 'simplecov'
|
5
|
+
require 'coveralls'
|
6
|
+
require 'stringio'
|
7
|
+
|
8
|
+
|
9
|
+
VCR.configure do |c|
|
10
|
+
c.cassette_library_dir = 'spec/support/cassettes'
|
11
|
+
c.hook_into :webmock
|
12
|
+
c.configure_rspec_metadata!
|
13
|
+
end
|
14
|
+
|
15
|
+
if ENV.include?('TRAVIS')
|
16
|
+
Coveralls.wear!
|
17
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
18
|
+
end
|
19
|
+
|
20
|
+
SimpleCov.start do
|
21
|
+
add_group 'Source', 'lib'
|
22
|
+
add_group 'Unit tests', 'spec/my_episodes'
|
23
|
+
add_group 'Integration tests', 'spec/integration'
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'my_episodes'
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: my_episodes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mathias Söderberg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mechanize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.7'
|
27
|
+
description: Export history from myepisodes.com
|
28
|
+
email: mths@sdrbrg.se
|
29
|
+
executables:
|
30
|
+
- myeps
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- bin/myeps
|
36
|
+
- lib/my_episodes.rb
|
37
|
+
- lib/my_episodes/cli.rb
|
38
|
+
- lib/my_episodes/client.rb
|
39
|
+
- lib/my_episodes/dump.rb
|
40
|
+
- lib/my_episodes/episode.rb
|
41
|
+
- lib/my_episodes/season.rb
|
42
|
+
- lib/my_episodes/show.rb
|
43
|
+
- lib/my_episodes/shows.rb
|
44
|
+
- lib/my_episodes/table_parser.rb
|
45
|
+
- lib/my_episodes/version.rb
|
46
|
+
- spec/integration/cli_integration_spec.rb
|
47
|
+
- spec/integration/client_integration_spec.rb
|
48
|
+
- spec/my_episodes/episode_spec.rb
|
49
|
+
- spec/my_episodes/season_spec.rb
|
50
|
+
- spec/my_episodes/show_spec.rb
|
51
|
+
- spec/spec_helper.rb
|
52
|
+
homepage: https://github.com/mthssdrbrg/my_episodes
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.9.3
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.2.2
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Simple myepisodes.com client
|
76
|
+
test_files:
|
77
|
+
- spec/integration/cli_integration_spec.rb
|
78
|
+
- spec/integration/client_integration_spec.rb
|
79
|
+
- spec/my_episodes/episode_spec.rb
|
80
|
+
- spec/my_episodes/season_spec.rb
|
81
|
+
- spec/my_episodes/show_spec.rb
|
82
|
+
- spec/spec_helper.rb
|