myshows 0.1.1 → 0.2.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.
data.tar.gz.sig CHANGED
Binary file
data/Manifest CHANGED
@@ -1,7 +1,6 @@
1
1
  Manifest
2
2
  README.rdoc
3
3
  Rakefile
4
- all-api.rb
5
4
  lib/myshows.rb
6
5
  lib/myshows/api.rb
7
6
  lib/myshows/episode.rb
@@ -9,5 +8,7 @@ lib/myshows/item.rb
9
8
  lib/myshows/profile.rb
10
9
  lib/myshows/search.rb
11
10
  lib/myshows/show.rb
11
+ lib/myshows/title_matcher.rb
12
12
  myshows.gemspec
13
13
  spec/myshows_spec.rb
14
+ spec/title_matcher_spec.rb
@@ -11,6 +11,10 @@ And use
11
11
  profile = MyShows::Profile.new 'demo', 'fe01ce2a7fbac8fafaed7c982a04e229'
12
12
  profile.shows # => all user shows
13
13
 
14
+ # you can find show by title among user shows (smart search)
15
+ profile.show 'big bang theory' # => show "The Big Bang Theory"
16
+ profile.show 'TBBT' # => show "The Big Bang Theory"
17
+
14
18
  search = MyShows::Search.new
15
19
  tbbt = search.show("The Big Bang Theory").first
16
20
  tbbt.title # => "The Big Bang Theory"
@@ -18,6 +22,10 @@ And use
18
22
  # and many other fields provided by API
19
23
 
20
24
  tbbt.episodes # => [...] - all episodes
25
+
26
+ # you can find episode by title (smart search)
27
+ pilot = tbbt.episode "the pilot episode"
28
+ # or by season and episode number
21
29
  pilot = tbbt.episode 1, 1
22
30
  pilot.title # => "Pilot"
23
31
  pilot.show # => tbbt
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('myshows', '0.1.1') do |p|
5
+ Echoe.new('myshows', '0.2.0') do |p|
6
6
  p.summary = "MyShows API"
7
7
  p.description = "Object-oriented wrapper over API of http://myshows.ru"
8
8
  p.url = "http://github.com/cypok/myshows"
@@ -1,4 +1,5 @@
1
1
  require 'myshows/api'
2
+ require 'myshows/title_matcher'
2
3
 
3
4
  module MyShows
4
5
  # Profile initialization is needed to gain access
@@ -18,5 +19,24 @@ module MyShows
18
19
  def shows
19
20
  @api.user_shows
20
21
  end
22
+
23
+ # Smart search of one of user shows by title
24
+ def show(title)
25
+ found = shows.values_at *matcher.match(title)
26
+ case found.count
27
+ when 0
28
+ raise MyShows::Error.new "show with title \"#{title}\" was not found"
29
+ when 1
30
+ found.first
31
+ else
32
+ raise MyShows::Error.new "ambiguous title \"#{title}\" corresponds to shows #{found.map {|s| %Q["#{s}"]} * ', '}"
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def matcher
39
+ @matcher ||= MyShows::TitleMatcher.new shows.map(&:title)
40
+ end
21
41
  end
22
42
  end
@@ -1,4 +1,5 @@
1
1
  require 'myshows/item'
2
+ require 'myshows/title_matcher'
2
3
 
3
4
  module MyShows
4
5
  # Provides such methods:
@@ -36,9 +37,39 @@ module MyShows
36
37
  @api.show_episodes self
37
38
  end
38
39
 
39
- # Returns episode by season and episode number
40
- def episode(season_number, episode_number)
41
- episodes.detect {|e| e.season_number == season_number && e.episode_number == episode_number }
40
+ # Returns episode by title (smart search) or by season and episode number
41
+ def episode(*args)
42
+ if args.first.is_a? String
43
+ episode_by_title *args
44
+ else
45
+ episode_by_numbers *args
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ def episode_by_title(title)
52
+ found = episodes.values_at *matcher.match(title)
53
+ case found.count
54
+ when 0
55
+ raise MyShows::Error.new "episode with title \"#{title}\" was not found in show #{self}"
56
+ when 1
57
+ found.first
58
+ else
59
+ raise MyShows::Error.new "ambiguous title \"#{title}\" corresponds to episodes #{found.map {|s| %Q["#{s}"]} * ', '}"
60
+ end
61
+ end
62
+
63
+ def matcher
64
+ @matcher ||= MyShows::TitleMatcher.new episodes.map(&:title)
65
+ end
66
+
67
+ def episode_by_numbers(season_number, episode_number)
68
+ episodes.detect {|e| e.season_number == season_number && e.episode_number == episode_number }.tap do |e|
69
+ if e.nil?
70
+ raise MyShows::Error.new "episode with number #{season_number}x#{episode_number} was not found in show #{self}"
71
+ end
72
+ end
42
73
  end
43
74
  end
44
75
  end
@@ -0,0 +1,67 @@
1
+ module MyShows
2
+ class TitleMatcher
3
+ # You could extend this list
4
+ MATCHING_PROCS = [
5
+ # simple
6
+ proc {|src, title| src == title },
7
+
8
+ # flash forward => FlashForward
9
+ # lietome => Lie to Me
10
+ proc {|src, title| src.gsub(' ', '') == title.gsub(' ', '') },
11
+
12
+ # tbbt => The Big Bang Theory
13
+ proc {|src, title| src == title.split.map{|w| w[0,1]} * '' },
14
+
15
+ # avatar => Avatar: The Last Airbender
16
+ proc {|src, title| title.split.include? src },
17
+
18
+ # house m d => House
19
+ proc {|src, title| src.split.include? title },
20
+ ]
21
+
22
+ def initialize(titles)
23
+ @titles = titles.map {|s| strip(s)}
24
+ @titles_wo_articles = @titles.map {|t| wo_articles(t) }
25
+ end
26
+
27
+ # Tries to find title closest to src,
28
+ # returns list of indexes of titles
29
+ def match(src)
30
+ matched = []
31
+
32
+ src = strip(src)
33
+
34
+
35
+ MATCHING_PROCS.each do |matcher|
36
+ @titles.each_with_index do |title, i|
37
+ matched << i if matcher.call(src, title)
38
+ end
39
+ return matched unless matched.empty?
40
+ end
41
+
42
+ src_wo_articles = wo_articles(src)
43
+ MATCHING_PROCS.each do |matcher|
44
+ @titles_wo_articles.each_with_index do |title_wo_articles, i|
45
+ matched << i if matcher.call(src_wo_articles, title_wo_articles)
46
+ end
47
+ return matched unless matched.empty?
48
+ end
49
+
50
+ []
51
+ end
52
+
53
+ private
54
+
55
+ def strip(s)
56
+ s.clone.downcase.
57
+ gsub(/\(\d{4}\)/, ''). # to clean "V (2009)"
58
+ gsub(/\([A-Za-z]{2}\)/, ''). # to clean "Bless This House (US)"
59
+ gsub(/[^A-Za-z0-9]/, ' '). # to clean "Star Wars: the Clone Wars"
60
+ gsub(/ +/, ' ').strip # remove extra spaces
61
+ end
62
+
63
+ def wo_articles(str)
64
+ str.split.reject {|w| w =~ /^the|an?$/ } * ' '
65
+ end
66
+ end
67
+ end
@@ -2,16 +2,16 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{myshows}
5
- s.version = "0.1.1"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Vladimir Parfinenko"]
9
9
  s.cert_chain = ["/Users/cypok/.gem/gem-public_cert.pem"]
10
- s.date = %q{2011-01-26}
10
+ s.date = %q{2011-01-29}
11
11
  s.description = %q{Object-oriented wrapper over API of http://myshows.ru}
12
12
  s.email = %q{vladimir.parfinenko@gmail.com}
13
- s.extra_rdoc_files = ["README.rdoc", "lib/myshows.rb", "lib/myshows/api.rb", "lib/myshows/episode.rb", "lib/myshows/item.rb", "lib/myshows/profile.rb", "lib/myshows/search.rb", "lib/myshows/show.rb"]
14
- s.files = ["Manifest", "README.rdoc", "Rakefile", "lib/myshows.rb", "lib/myshows/api.rb", "lib/myshows/episode.rb", "lib/myshows/item.rb", "lib/myshows/profile.rb", "lib/myshows/search.rb", "lib/myshows/show.rb", "myshows.gemspec", "spec/myshows_spec.rb"]
13
+ s.extra_rdoc_files = ["README.rdoc", "lib/myshows.rb", "lib/myshows/api.rb", "lib/myshows/episode.rb", "lib/myshows/item.rb", "lib/myshows/profile.rb", "lib/myshows/search.rb", "lib/myshows/show.rb", "lib/myshows/title_matcher.rb"]
14
+ s.files = ["Manifest", "README.rdoc", "Rakefile", "lib/myshows.rb", "lib/myshows/api.rb", "lib/myshows/episode.rb", "lib/myshows/item.rb", "lib/myshows/profile.rb", "lib/myshows/search.rb", "lib/myshows/show.rb", "lib/myshows/title_matcher.rb", "myshows.gemspec", "spec/myshows_spec.rb", "spec/title_matcher_spec.rb"]
15
15
  s.homepage = %q{http://github.com/cypok/myshows}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Myshows", "--main", "README.rdoc"]
17
17
  s.require_paths = ["lib"]
@@ -11,6 +11,24 @@ describe MyShows do
11
11
  it "should return user shows" do
12
12
  @profile.shows.map(&:title).should include TBBT
13
13
  end
14
+
15
+ describe "#show" do
16
+ it "should find user show by title" do
17
+ @profile.show('TBBT').title.should == TBBT
18
+ end
19
+
20
+ it "should raise exception if show was not found" do
21
+ lambda do
22
+ @profile.show('WTF?')
23
+ end.should raise_error MyShows::Error
24
+ end
25
+
26
+ it "should raise exception if title is ambiguous" do
27
+ lambda do
28
+ @profile.show('The')
29
+ end.should raise_error MyShows::Error
30
+ end
31
+ end
14
32
  end
15
33
 
16
34
  describe MyShows::Search do
@@ -42,10 +60,38 @@ describe MyShows do
42
60
  @show.episodes.should have_at_least(10).items
43
61
  end
44
62
 
45
- it "should find episode by season and episode number" do
46
- e = @show.episode(1, 2)
47
- e.season_number.should == 1
48
- e.episode_number.should == 2
63
+ describe "#episode" do
64
+
65
+ it "should find episode by season and episode number" do
66
+ e = @show.episode(1, 2)
67
+ e.season_number.should == 1
68
+ e.episode_number.should == 2
69
+ end
70
+
71
+ it "should find episode by title" do
72
+ e = @show.episode("pilot")
73
+ e.title.should == "Pilot"
74
+ e.season_number.should == 1
75
+ e.episode_number.should == 1
76
+ end
77
+
78
+ it "should raise exception if nothing was found" do
79
+ lambda do
80
+ @show.episode(99, 99)
81
+ end.should raise_error MyShows::Error
82
+ end
83
+
84
+ it "should raise exception if nothing was found" do
85
+ lambda do
86
+ @show.episode "Non existent episode"
87
+ end.should raise_error MyShows::Error
88
+ end
89
+
90
+ it "should raise exception if title is ambiguous" do
91
+ lambda do
92
+ @show.episode "The"
93
+ end.should raise_error MyShows::Error
94
+ end
49
95
  end
50
96
  end
51
97
 
@@ -0,0 +1,83 @@
1
+ require 'myshows'
2
+
3
+ describe MyShows::TitleMatcher do
4
+ before :all do
5
+ @titles = <<-END
6
+ V (2009)
7
+ Star Wars: The Clone Wars (2008)
8
+ Over There (US)
9
+ South Park
10
+ Lost
11
+ How I Met Your Mother
12
+ The IT Crowd
13
+ Due South (1994)
14
+ The Big Bang Theory
15
+ Life on Mars (UK)
16
+ Lie to Me
17
+ Avatar: The Last Airbender
18
+ FlashForward
19
+ Desperate Housewifes
20
+ END
21
+ @titles = @titles.split "\n"
22
+
23
+ @matcher = MyShows::TitleMatcher.new @titles
24
+ end
25
+
26
+ it "should find nothing" do
27
+ match("Unknown series").should be_empty
28
+ end
29
+
30
+ it "should simply find" do
31
+ match("Lost").should == ["Lost"]
32
+ end
33
+
34
+ it "should ingore year of series start" do
35
+ match("V").should == ["V (2009)"]
36
+ end
37
+
38
+ it "should ingore country of series creation" do
39
+ match("Over There").should == ["Over There (US)"]
40
+ end
41
+
42
+ it "should ignore all but letters" do
43
+ match("Star Wars The Clone Wars").should == ["Star Wars: The Clone Wars (2008)"]
44
+ end
45
+
46
+ it "should find ambiguous titles" do
47
+ match("South").should == ["South Park", "Due South (1994)"]
48
+ end
49
+
50
+ it "should find separeted title" do
51
+ match("Flash Forward").should == ["FlashForward"]
52
+ end
53
+
54
+ it "should find joined title" do
55
+ match("lietome").should == ["Lie to Me"]
56
+ end
57
+
58
+ it "should find some part of title in src" do
59
+ match("Avatar").should == ["Avatar: The Last Airbender"]
60
+ end
61
+
62
+ it "should find abbreviated title" do
63
+ match("TBBT").should == ["The Big Bang Theory"]
64
+ end
65
+
66
+ it "should find title with extra article" do
67
+ match("Life on a Mars").should == ["Life on Mars (UK)"]
68
+ end
69
+
70
+ it "should find title without article" do
71
+ match("IT Crowd").should == ["The IT Crowd"]
72
+ end
73
+
74
+ it "should find title with wrong article" do
75
+ match("An IT Crowd").should == ["The IT Crowd"]
76
+ end
77
+
78
+ protected
79
+
80
+ def match(src)
81
+ @titles.values_at *@matcher.match(src)
82
+ end
83
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myshows
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Vladimir Parfinenko
@@ -36,7 +36,7 @@ cert_chain:
36
36
  78F0qvtLjR0DAnN6uYs98PR+jHE+0ckLX3D9sw==
37
37
  -----END CERTIFICATE-----
38
38
 
39
- date: 2011-01-26 00:00:00 +06:00
39
+ date: 2011-01-29 00:00:00 +06:00
40
40
  default_executable:
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
@@ -110,6 +110,7 @@ extra_rdoc_files:
110
110
  - lib/myshows/profile.rb
111
111
  - lib/myshows/search.rb
112
112
  - lib/myshows/show.rb
113
+ - lib/myshows/title_matcher.rb
113
114
  files:
114
115
  - Manifest
115
116
  - README.rdoc
@@ -121,8 +122,10 @@ files:
121
122
  - lib/myshows/profile.rb
122
123
  - lib/myshows/search.rb
123
124
  - lib/myshows/show.rb
125
+ - lib/myshows/title_matcher.rb
124
126
  - myshows.gemspec
125
127
  - spec/myshows_spec.rb
128
+ - spec/title_matcher_spec.rb
126
129
  has_rdoc: true
127
130
  homepage: http://github.com/cypok/myshows
128
131
  licenses: []
metadata.gz.sig CHANGED
Binary file