splattael-shoutcast_api 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest CHANGED
@@ -1,4 +1,6 @@
1
1
  Rakefile
2
+ bin/shoutcast_search
3
+ lib/delegator.rb
2
4
  lib/shoutcast_api.rb
3
5
  README.rdoc
4
6
  Manifest
@@ -9,4 +11,5 @@ test/fixtures/genrelist.plain
9
11
  test/fixtures/search_death.plain
10
12
  test/fixtures/empty.plain
11
13
  test/test_basic.rb
14
+ test/test_delegator.rb
12
15
  shoutcast_api.gemspec
data/README.rdoc CHANGED
@@ -8,7 +8,7 @@ Uses httparty and roxml for fetch and parsing data from http://yp.shoutcast.com/
8
8
  require 'shoutcast_api'
9
9
 
10
10
  # Stations
11
- Shoutcast.search(:name => "Chronix").stations.each do |station|
11
+ Shoutcast.search(:name => "Chronix").each do |station|
12
12
  p station
13
13
  puts station.tunein
14
14
  end
@@ -17,7 +17,6 @@ Uses httparty and roxml for fetch and parsing data from http://yp.shoutcast.com/
17
17
 
18
18
  # Genres
19
19
  Shoutcast.genres # => all genres
20
- Shoutcast.genres /op/ # => genres which match "op"
21
20
 
22
21
  == Command line
23
22
 
@@ -28,7 +27,5 @@ Uses httparty and roxml for fetch and parsing data from http://yp.shoutcast.com/
28
27
 
29
28
  == TODO
30
29
 
31
- * Tests!
32
30
  * Docs!
33
- * command line options transformations
34
- * handle empty responses like search(:search => "")
31
+ * command line options transformations
data/Rakefile CHANGED
@@ -6,12 +6,23 @@ desc 'Default: run unit tests.'
6
6
  task :default => :test
7
7
 
8
8
  require 'echoe'
9
-
10
9
  Echoe.new('shoutcast_api') do |gem|
11
- gem.version = '0.0.2'
10
+ gem.version = '0.1.0'
12
11
  gem.author = 'Peter Suschlik'
13
12
  gem.summary = 'Simple Shoutcast.com API.'
14
13
  gem.email = 'peter-scapi@suschlik.de'
15
14
  gem.url = %q{http://github.com/splattael/shoutcast_api}
16
15
  gem.runtime_dependencies = [ "httparty ~>0.4", "roxml ~>2.5" ]
16
+ gem.ignore_pattern = ["tags"]
17
+ end
18
+
19
+ desc "Tag files for vim"
20
+ task :ctags do
21
+ dirs = $LOAD_PATH.select {|path| File.directory?(path) }
22
+ system "ctags -R #{dirs.join(" ")}"
23
+ end
24
+
25
+ desc "Find whitespace at line ends"
26
+ task :eol do
27
+ system "grep -nrE ' +$' *"
17
28
  end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require 'shoutcast_api'
6
+
7
+ if ARGV.size > 0
8
+ options = ARGV.inject({}) do |hash, arg|
9
+ key, value = arg.split(/=/)
10
+ hash[key.intern] = value
11
+ hash
12
+ end
13
+ # TODO do more transformations
14
+ # br -> bitrate
15
+ # ct -> current_title
16
+ # can ROXML help us?
17
+ options[:search] = options.delete(:name) if options[:name]
18
+
19
+ puts Shoutcast::Station.header
20
+ Shoutcast.search(options).sort.reverse.each do |station|
21
+ puts station
22
+ end
23
+ else
24
+ puts "Genres: ", Shoutcast.genres.map { |g| g.name }.join(", ")
25
+ end
26
+
data/lib/delegator.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Shoutcast
2
+
3
+ # Delegates methods via Forwardable.
4
+ module Delegator
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ # Delegates all methods defined +klass+ to +object+.
11
+ #
12
+ # === Usage
13
+ # class ArrayMimic < SomeInheritance
14
+ #
15
+ # delegate_all :@array, Array
16
+ #
17
+ # end
18
+ #
19
+ # array = ArrayMimic.new
20
+ # array.push 1, 2, 3
21
+ # array.each do |item|
22
+ # p item
23
+ # end
24
+ #
25
+ def delegate_all(object, klass)
26
+ class_eval <<-EVAL
27
+ extend Forwardable
28
+
29
+ def_delegators :#{object}, *(#{klass}.instance_methods - instance_methods)
30
+ EVAL
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
data/lib/shoutcast_api.rb CHANGED
@@ -2,46 +2,58 @@ require 'httparty'
2
2
  require 'roxml'
3
3
  require 'forwardable'
4
4
 
5
+ require 'delegator'
6
+
5
7
  module Shoutcast
6
8
  extend Forwardable
7
9
  extend self
8
10
 
9
- def_delegators :Fetcher, :genre, :search
11
+ def_delegators :Fetcher, :genres, :search
10
12
 
11
13
  class Fetcher
12
14
  include HTTParty
13
- base_uri 'http://yp.shoutcast.com/sbin/newxml.phtml'
15
+ base_uri "http://yp.shoutcast.com"
14
16
  format :plain
15
17
 
16
- def self.genres(filter=nil)
17
- list = Genrelist.from_xml fetch
18
-
19
- list.filter(filter)
18
+ def self.genres
19
+ fetch do |xml|
20
+ Genrelist.from_xml xml
21
+ end
20
22
  end
21
23
 
22
24
  def self.search(options={})
23
- Stationlist.from_xml fetch(options)
25
+ fetch(options) do |xml|
26
+ Stationlist.from_xml xml
27
+ end
24
28
  end
25
29
 
26
30
  private
27
31
 
28
- def self.fetch(options={})
29
- options.update(:nocache => Time.now.to_f)
30
- get('', :query => options).body
32
+ def self.fetch(options={}, &block)
33
+ options.update(:nocache => Time.now.to_f) if options
34
+ data = get("/sbin/newxml.phtml", :query => options).body
35
+
36
+ block.call(data) unless data.empty?
31
37
  end
32
38
  end
33
39
 
34
40
  # XML
35
41
 
36
- class Base
37
- include ROXML
42
+ module Xml
43
+ def self.included(base)
44
+ base.send(:include, ROXML)
45
+ base.extend ClassMethods
46
+ end
38
47
 
39
- def self.trim
40
- proc { |v| v.to_s.strip }
48
+ module ClassMethods
49
+ def trim
50
+ proc { |v| v.to_s.strip.squeeze(" ") }
51
+ end
41
52
  end
42
53
  end
43
54
 
44
- class Station < Base
55
+ class Station
56
+ include Xml
45
57
  # <station
46
58
  # id="423873"
47
59
  # name="www.deathmetal.at"
@@ -79,26 +91,36 @@ module Shoutcast
79
91
  end
80
92
  end
81
93
 
82
- class Stationlist < Base
94
+ class Stationlist
95
+ include Xml
96
+ include Delegator
97
+
98
+ delegate_all :@stations, Array
99
+
83
100
  # <tunein base="/sbin/tunein-station.pls"/>
84
101
  # <station... /> <station .../>
85
- BASE_URL = "http://yp.shoutcast.com"
86
102
 
87
103
  xml_reader :tunein_base_path, :from => '@base', :in => 'tunein'
88
- xml_reader :stations, :as => [ Station ]
104
+ xml_attr :stations, :as => [ Station ]
105
+
106
+ class << self
107
+ attr_accessor :base_uri
108
+ end
109
+ self.base_uri = Fetcher.base_uri
89
110
 
90
111
  def tunein(station)
91
- "#{BASE_URL}#{tunein_base_path}?id=#{station.id}"
112
+ "#{self.class.base_uri}#{tunein_base_path}?id=#{station.id}"
92
113
  end
93
114
 
94
115
  private
95
116
 
96
117
  def after_parse
97
- stations.each { |station| station.tunein = tunein(station) }
118
+ each { |station| station.tunein = tunein(station) }
98
119
  end
99
120
  end
100
121
 
101
- class Genre < Base
122
+ class Genre
123
+ include Xml
102
124
  # <genre name="24h"/>
103
125
  xml_reader :name, :from => :attr
104
126
 
@@ -107,44 +129,14 @@ module Shoutcast
107
129
  end
108
130
  end
109
131
 
110
- class Genrelist < Base
111
- xml_reader :genres, :as => [ Genre ]
112
-
113
- def filter(option=nil)
114
- case option
115
- when String
116
- genres.select { |genre| genre.name == option }
117
- when Regexp
118
- genres.select { |genre| genre.name =~ option }
119
- else
120
- genres
121
- end
122
- end
123
- end
132
+ class Genrelist
133
+ include Xml
134
+ include Delegator
124
135
 
125
- end
136
+ delegate_all :@genres, Array
126
137
 
127
- if $0 == __FILE__
128
- require 'pp'
138
+ xml_attr :genres, :as => [ Genre ]
129
139
 
130
- if ARGV.size > 0
131
- options = ARGV.inject({}) do |hash, arg|
132
- key, value = arg.split(/=/)
133
- hash[key.intern] = value
134
- hash
135
- end
136
- # TODO do more transformations
137
- # br -> bitrate
138
- # ct -> current_title
139
- # can ROXML help us?
140
- options[:search] = options.delete(:name) if options[:name]
141
-
142
- puts Shoutcast::Station.header
143
- Shoutcast.search(options).stations.sort.reverse.each do |station|
144
- puts station
145
- end
146
- else
147
- pp Shoutcast.genres
148
140
  end
149
141
 
150
142
  end
@@ -2,15 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{shoutcast_api}
5
- s.version = "0.0.2"
5
+ s.version = "0.1.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 = ["Peter Suschlik"]
9
9
  s.date = %q{2009-05-14}
10
+ s.default_executable = %q{shoutcast_search}
10
11
  s.description = %q{Simple Shoutcast.com API.}
11
12
  s.email = %q{peter-scapi@suschlik.de}
12
- s.extra_rdoc_files = ["lib/shoutcast_api.rb", "README.rdoc"]
13
- s.files = ["Rakefile", "lib/shoutcast_api.rb", "README.rdoc", "Manifest", "test/test_xml.rb", "test/helper.rb", "test/test_fetcher.rb", "test/fixtures/genrelist.plain", "test/fixtures/search_death.plain", "test/fixtures/empty.plain", "test/test_basic.rb", "shoutcast_api.gemspec"]
13
+ s.executables = ["shoutcast_search"]
14
+ s.extra_rdoc_files = ["bin/shoutcast_search", "lib/delegator.rb", "lib/shoutcast_api.rb", "README.rdoc"]
15
+ s.files = ["Rakefile", "bin/shoutcast_search", "lib/delegator.rb", "lib/shoutcast_api.rb", "README.rdoc", "Manifest", "test/test_xml.rb", "test/helper.rb", "test/test_fetcher.rb", "test/fixtures/genrelist.plain", "test/fixtures/search_death.plain", "test/fixtures/empty.plain", "test/test_basic.rb", "test/test_delegator.rb", "shoutcast_api.gemspec"]
14
16
  s.has_rdoc = true
15
17
  s.homepage = %q{http://github.com/splattael/shoutcast_api}
16
18
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Shoutcast_api", "--main", "README.rdoc"]
@@ -18,7 +20,7 @@ Gem::Specification.new do |s|
18
20
  s.rubyforge_project = %q{shoutcast_api}
19
21
  s.rubygems_version = %q{1.3.1}
20
22
  s.summary = %q{Simple Shoutcast.com API.}
21
- s.test_files = ["test/test_xml.rb", "test/test_fetcher.rb", "test/test_basic.rb"]
23
+ s.test_files = ["test/test_xml.rb", "test/test_fetcher.rb", "test/test_basic.rb", "test/test_delegator.rb"]
22
24
 
23
25
  if s.respond_to? :specification_version then
24
26
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
data/test/test_basic.rb CHANGED
@@ -1,5 +1,14 @@
1
1
  require File.join(File.dirname(__FILE__), 'helper')
2
2
 
3
+ class ShoutcastTest < Test::Unit::TestCase
4
+
5
+ def test_delegators
6
+ assert_respond_to Shoutcast, :genres
7
+ assert_respond_to Shoutcast, :search
8
+ end
9
+
10
+ end
11
+
3
12
  class ExtensionsTest < Test::Unit::TestCase
4
13
 
5
14
  def test_file_fixture
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class ArrayMimic
4
+ include Shoutcast::Delegator
5
+
6
+ delegate_all :@array, Array
7
+
8
+ def initialize
9
+ @array = []
10
+ end
11
+
12
+ end
13
+
14
+ class DelegatorTest < Test::Unit::TestCase
15
+
16
+ def test_method_delegation
17
+ ary = ArrayMimic.new
18
+ ary.push 1, 2, 3
19
+
20
+ assert_equal 3, ary.size
21
+ assert_equal 1, ary.first
22
+ assert_equal 3, ary.last
23
+ assert_respond_to ary, :each
24
+ assert_equal [].methods.sort, ary.methods.sort
25
+ end
26
+
27
+ end
data/test/test_fetcher.rb CHANGED
@@ -1,3 +1,41 @@
1
1
  require File.join(File.dirname(__FILE__), 'helper')
2
2
 
3
- # TODO integeration tests with fetcher.
3
+ class FetcherTest < Test::Unit::TestCase
4
+ include Shoutcast
5
+
6
+ def test_base_uri
7
+ assert_equal "http://yp.shoutcast.com", Stationlist.base_uri
8
+ end
9
+
10
+ def test_genres_list
11
+ stub_http_response_with("genrelist.plain")
12
+
13
+ list = Fetcher.genres
14
+ # DRY test/text_xml.rb GenrelistTest
15
+ assert_instance_of Genrelist, list
16
+ assert_instance_of Genre, list.first
17
+ end
18
+
19
+ def test_genres_empty_response
20
+ stub_http_response_with("empty.plain")
21
+
22
+ assert_nil Fetcher.genres
23
+ end
24
+
25
+ def test_search_with_invalid_option
26
+ stub_http_response_with("search_death.plain")
27
+
28
+ list = Fetcher.search
29
+ # DRY test/text_xml.rb StationlistTest
30
+ assert_instance_of Stationlist, list
31
+ assert_equal "/sbin/tunein-station.pls", list.tunein_base_path
32
+ assert_instance_of Station, list.first
33
+ end
34
+
35
+ def test_search_empty_response
36
+ stub_http_response_with("empty.plain")
37
+
38
+ assert_nil Fetcher.genres
39
+ end
40
+
41
+ end
data/test/test_xml.rb CHANGED
@@ -64,6 +64,7 @@ class StationTest < Test::Unit::TestCase
64
64
 
65
65
  end
66
66
 
67
+
67
68
  class StationlistTest < Test::Unit::TestCase
68
69
  include Shoutcast
69
70
 
@@ -74,19 +75,22 @@ class StationlistTest < Test::Unit::TestCase
74
75
  def test_attributes
75
76
  assert_instance_of Stationlist, @list
76
77
  assert_equal "/sbin/tunein-station.pls", @list.tunein_base_path
77
- assert_instance_of Array, @list.stations
78
- assert_equal 49, @list.stations.size
79
- assert_instance_of Station, @list.stations.first
78
+ assert_equal 49, @list.size
79
+ assert_instance_of Station, @list.first
80
+ end
81
+
82
+ def test_base_uri
83
+ assert_equal "http://yp.shoutcast.com", Stationlist.base_uri
80
84
  end
81
85
 
82
86
  def test_tunein
83
- station = @list.stations.first
87
+ station = @list.first
84
88
 
85
89
  assert_equal "http://yp.shoutcast.com/sbin/tunein-station.pls?id=#{station.id}", @list.tunein(station)
86
90
  end
87
91
 
88
92
  def test_tunein_propagtion_after_parse
89
- station = @list.stations.first
93
+ station = @list.first
90
94
 
91
95
  assert_equal @list.tunein(station), station.tunein
92
96
  end
@@ -101,28 +105,35 @@ class GenrelistTest < Test::Unit::TestCase
101
105
  @list = Genrelist.from_xml file_fixture("genrelist.plain")
102
106
  end
103
107
 
104
- def test_parsing
108
+ def test_array_mimic
105
109
  assert_instance_of Genrelist, @list
106
- assert_instance_of Array, @list.genres
107
- assert_equal 434, @list.genres.size
108
- assert_instance_of Genre, @list.genres.first
109
- end
110
-
111
- def test_filter
112
- assert_equal @list.genres, @list.filter
113
-
114
- assert_equal 1, @list.filter("Rock").size
115
- assert_equal "Rock", @list.filter("Rock").first.name
116
-
117
- assert_equal 13, @list.filter(/op/).size
118
- assert_equal "Autopilot", @list.filter(/op/).first.name
110
+ assert_equal 434, @list.size
111
+ assert_instance_of Genre, @list.first
112
+ assert_respond_to @list, :each
113
+ assert_raise(NoMethodError) { @list.nothing_found }
119
114
  end
120
115
 
121
116
  def test_genre_name
122
- sorted = @list.genres.sort
117
+ sorted = @list.sort
123
118
 
124
119
  assert_equal "24h", sorted.first.name
125
120
  assert_equal "Zouk", sorted.last.name
126
121
  end
127
122
 
128
123
  end
124
+
125
+
126
+ class MyClass
127
+ include Shoutcast::Xml
128
+ end
129
+
130
+ class XmlTest < Test::Unit::TestCase
131
+
132
+ def test_trim
133
+ assert_respond_to MyClass, :trim
134
+
135
+ string = " test mee "
136
+ assert_equal "test mee", MyClass.trim.call(string)
137
+ end
138
+
139
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splattael-shoutcast_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Suschlik
@@ -10,7 +10,7 @@ bindir: bin
10
10
  cert_chain: []
11
11
 
12
12
  date: 2009-05-14 00:00:00 -07:00
13
- default_executable:
13
+ default_executable: shoutcast_search
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -34,15 +34,19 @@ dependencies:
34
34
  version:
35
35
  description: Simple Shoutcast.com API.
36
36
  email: peter-scapi@suschlik.de
37
- executables: []
38
-
37
+ executables:
38
+ - shoutcast_search
39
39
  extensions: []
40
40
 
41
41
  extra_rdoc_files:
42
+ - bin/shoutcast_search
43
+ - lib/delegator.rb
42
44
  - lib/shoutcast_api.rb
43
45
  - README.rdoc
44
46
  files:
45
47
  - Rakefile
48
+ - bin/shoutcast_search
49
+ - lib/delegator.rb
46
50
  - lib/shoutcast_api.rb
47
51
  - README.rdoc
48
52
  - Manifest
@@ -53,6 +57,7 @@ files:
53
57
  - test/fixtures/search_death.plain
54
58
  - test/fixtures/empty.plain
55
59
  - test/test_basic.rb
60
+ - test/test_delegator.rb
56
61
  - shoutcast_api.gemspec
57
62
  has_rdoc: true
58
63
  homepage: http://github.com/splattael/shoutcast_api
@@ -89,3 +94,4 @@ test_files:
89
94
  - test/test_xml.rb
90
95
  - test/test_fetcher.rb
91
96
  - test/test_basic.rb
97
+ - test/test_delegator.rb