rubyyot-razsell 0.0.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/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +10 -0
- data/Rakefile +68 -0
- data/VERSION +1 -0
- data/docs/RSSGuide1.02.pdf +0 -0
- data/features/razsell.feature +18 -0
- data/features/step_definitions/razsell_steps.rb +40 -0
- data/features/support/env.rb +13 -0
- data/features/support/razsell_mixed_in.rb +5 -0
- data/lib/constants/image_sizes.rb +10 -0
- data/lib/constants/product_types.rb +30 -0
- data/lib/constants/sort_methods.rb +7 -0
- data/lib/constants/sort_periods.rb +9 -0
- data/lib/constants.rb +5 -0
- data/lib/engine.rb +25 -0
- data/lib/http_service.rb +8 -0
- data/lib/item.rb +19 -0
- data/lib/query.rb +107 -0
- data/lib/razsell.rb +11 -0
- data/lib/results.rb +70 -0
- data/test/engine_test.rb +55 -0
- data/test/fixtures/page_1.rss +125 -0
- data/test/fixtures/page_2.rss +87 -0
- data/test/fixtures/rockstar.rss +125 -0
- data/test/fixtures.rb +8 -0
- data/test/product_types_test.rb +35 -0
- data/test/query_test.rb +176 -0
- data/test/razsell_test.rb +19 -0
- data/test/results_test.rb +103 -0
- data/test/test_helper.rb +15 -0
- metadata +130 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jamal Hansen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
= razsell
|
2
|
+
|
3
|
+
A feed parser for Zazzle.com written in Ruby. Raszell allows you to query zazzle for information on products.
|
4
|
+
|
5
|
+
== Copyright
|
6
|
+
|
7
|
+
Copyright (c) 2009 Jamal Hansen.
|
8
|
+
Jamal Hansen and razsell are not affiliated with Zazzle.com.
|
9
|
+
Zazzle.com is Copyright (c) Zazzle.com.
|
10
|
+
See LICENSE for details for razsell.
|
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "razsell"
|
8
|
+
gem.summary = %Q{A gem for getting info about products on a website that has a similar name}
|
9
|
+
gem.email = "jamal.hansen@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/rubyyot/razsell"
|
11
|
+
gem.authors = ["Jamal Hansen"]
|
12
|
+
gem.add_dependency('hpricot')
|
13
|
+
gem.add_development_dependency('cucumber', '>= 0.3.11')
|
14
|
+
gem.add_development_dependency('mocha')
|
15
|
+
gem.add_development_dependency('technicalpickles-shoulda')
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rake/testtask'
|
24
|
+
Rake::TestTask.new(:test) do |test|
|
25
|
+
test.libs << 'lib' << 'test'
|
26
|
+
test.pattern = 'test/**/*_test.rb'
|
27
|
+
test.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'rcov/rcovtask'
|
32
|
+
Rcov::RcovTask.new do |test|
|
33
|
+
test.libs << 'test'
|
34
|
+
test.pattern = 'test/**/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
rescue LoadError
|
38
|
+
task :rcov do
|
39
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
require 'cucumber/rake/task'
|
45
|
+
Cucumber::Rake::Task.new(:features)
|
46
|
+
rescue LoadError
|
47
|
+
task :features do
|
48
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
task :default => :test
|
53
|
+
|
54
|
+
require 'rake/rdoctask'
|
55
|
+
Rake::RDocTask.new do |rdoc|
|
56
|
+
if File.exist?('VERSION.yml')
|
57
|
+
config = YAML.load(File.read('VERSION.yml'))
|
58
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
59
|
+
else
|
60
|
+
version = ""
|
61
|
+
end
|
62
|
+
|
63
|
+
rdoc.rdoc_dir = 'rdoc'
|
64
|
+
rdoc.title = "razsell #{version}"
|
65
|
+
rdoc.rdoc_files.include('README*')
|
66
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
67
|
+
end
|
68
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
Binary file
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Feature: Query Image Link
|
2
|
+
In order to get a link to an image of a product
|
3
|
+
A user of razsell
|
4
|
+
Will query a product image link
|
5
|
+
|
6
|
+
Scenario: Querying the keyword Rockstar for the artist Kung Fu Tees returns an image url
|
7
|
+
Given a desire to query the artist "KungFuTees" on Zazzle
|
8
|
+
When I query "rockstar"
|
9
|
+
Then there should be "3" items
|
10
|
+
And the items should have the keyword "Rockstar"
|
11
|
+
And the items should have a link to an image
|
12
|
+
|
13
|
+
Scenario: Querying the keyword Rockstar for the artist Kung Fu Tees returns a thumbnail url
|
14
|
+
Given a desire to query the artist "KungFuTees" on Zazzle
|
15
|
+
When I query "rockstar"
|
16
|
+
Then there should be "3" items
|
17
|
+
And the items should have the keyword "Rockstar"
|
18
|
+
And the items should have a link to a thumbnail
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
Given /^a desire to query the artist "([^\"]*)" on Zazzle$/ do |artist|
|
3
|
+
@artist = artist
|
4
|
+
@sut = RazsellMixedIn.new
|
5
|
+
@query = Razsell::Query.new.for_artist(artist)
|
6
|
+
end
|
7
|
+
|
8
|
+
When /^the "([^\"]*)" is "([^\"]*)"$/ do |name, value|
|
9
|
+
@query.add_criteria(name, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I query "([^\"]*)"$/ do |fixture|
|
13
|
+
http_service = Razsell::HttpService.new
|
14
|
+
http_service.expects(:get).once.returns(feed(fixture))
|
15
|
+
|
16
|
+
@result = @sut.request(@query, :http_service => http_service)
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /^there should be "([^\"]*)" items/ do |item_count|
|
20
|
+
assert_equal item_count.to_i, @result.item_count
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^the items should have the keyword "([^\"]*)"$/ do |keyword|
|
24
|
+
@result.items.each do |item|
|
25
|
+
assert item.title.include? keyword
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the items should have a link to an image$/ do
|
30
|
+
@result.items.each do |item|
|
31
|
+
assert(/http:\/\/rlv.zcache.com\/.*\.jpg/ =~ item.content_url)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Then /^the items should have a link to a thumbnail$/ do
|
36
|
+
@result.items.each do |item|
|
37
|
+
assert(/http:\/\/rlv.zcache.com\/.*\.jpg/ =~ item.thumbnail_url)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../test')
|
3
|
+
require 'razsell'
|
4
|
+
require 'mocha'
|
5
|
+
require 'fixtures'
|
6
|
+
include Fixtures
|
7
|
+
|
8
|
+
require 'test/unit/assertions'
|
9
|
+
|
10
|
+
World(Test::Unit::Assertions)
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
require 'razsell_mixed_in'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module Razsell
|
3
|
+
module ProductTypes
|
4
|
+
BUMPER_STICKER ="128"
|
5
|
+
CARD = "137"
|
6
|
+
MOUSE_PAD = "144"
|
7
|
+
BUTTON = "145"
|
8
|
+
KEYCHAIN = "146"
|
9
|
+
MAGNET = "147"
|
10
|
+
HAT = "148"
|
11
|
+
BAG = "149"
|
12
|
+
TIE = "151"
|
13
|
+
PHOTO_SCULPTURE = "153"
|
14
|
+
APRON ="154"
|
15
|
+
PHOTO_PRINT = "156"
|
16
|
+
CALENDAR = "158"
|
17
|
+
SHOES = "167"
|
18
|
+
MUG = "168"
|
19
|
+
POSTAGE_STAMP = "172"
|
20
|
+
SKATEBOARD = "186"
|
21
|
+
STICKER = "217"
|
22
|
+
POSTER = "228"
|
23
|
+
EMBROIDERED_SHIRT = "231"
|
24
|
+
EMBROIDERED_BAG = "232"
|
25
|
+
EMBROIDERED_HAT = "233"
|
26
|
+
T_SHIRT = "235"
|
27
|
+
POSTCARD = "239"
|
28
|
+
PROFILE_CARD = "240"
|
29
|
+
end
|
30
|
+
end
|
data/lib/constants.rb
ADDED
data/lib/engine.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'http_service'
|
2
|
+
|
3
|
+
module Razsell
|
4
|
+
class Engine
|
5
|
+
def initialize opts={}
|
6
|
+
@http_service = get_http_service opts
|
7
|
+
end
|
8
|
+
|
9
|
+
def request query
|
10
|
+
feed = @http_service.get query
|
11
|
+
results = Razsell::Results.new feed
|
12
|
+
|
13
|
+
while results.has_more_pages? && query.advance_page
|
14
|
+
feed = @http_service.get query
|
15
|
+
results.add(feed)
|
16
|
+
end
|
17
|
+
|
18
|
+
results
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_http_service opts
|
22
|
+
opts[:http_service] ? opts[:http_service] : Razsell::HttpService.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/http_service.rb
ADDED
data/lib/item.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Razsell
|
3
|
+
class Item
|
4
|
+
def initialize values={}
|
5
|
+
@values = values
|
6
|
+
self.class.class_eval do
|
7
|
+
add_attributes values
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.add_attributes values
|
12
|
+
values.each_key do |key|
|
13
|
+
define_method "#{key.to_s}" do
|
14
|
+
@values[key]
|
15
|
+
end\
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/query.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'constants'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Razsell
|
5
|
+
class Query
|
6
|
+
include Razsell::SortMethods
|
7
|
+
include Razsell::ImageSizes
|
8
|
+
|
9
|
+
attr_accessor :page_limit
|
10
|
+
attr_reader :artist
|
11
|
+
|
12
|
+
def initialize *args
|
13
|
+
set_default_page_limit
|
14
|
+
\
|
15
|
+
default_criteria
|
16
|
+
|
17
|
+
@keys = { :keywords => 'qs', :product_line => 'cg', :product_type => 'pt',
|
18
|
+
:sort_type => 'st', :sort_period => 'sp', :page => 'pg', :items_per_page => 'ps',
|
19
|
+
:feed_type => 'ft', :image_size => 'isz', :image_background_color => 'bg',
|
20
|
+
:opensearch => 'opensearch', :source => 'src'}
|
21
|
+
end
|
22
|
+
|
23
|
+
def for_artist artist
|
24
|
+
@artist = artist
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
#def add_criteria name, value
|
29
|
+
# this.send name, value
|
30
|
+
#end
|
31
|
+
|
32
|
+
def base_url
|
33
|
+
return "http://feed.zazzle.com/#{@artist}/rss" if @artist
|
34
|
+
"http://feed.zazzle.com/rss"
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_querystring
|
38
|
+
@querystring.to_a.map { |a| build_pair(a) }.sort {|x,y| x <=> y }.delete_if { |pair| pair == "" }.join("&")
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_url
|
42
|
+
"#{base_url}?#{to_querystring}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def advance_page
|
46
|
+
return false if @querystring[:page] >= page_limit
|
47
|
+
|
48
|
+
@querystring[:page] = @querystring[:page] + 1
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def build_pair pair
|
54
|
+
return "#{get_querystring_identifier(pair[0])}=#{format_value(pair[1])}" if pair[1]
|
55
|
+
""
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_querystring_identifier key
|
59
|
+
@keys[key] if @keys[key]
|
60
|
+
end
|
61
|
+
|
62
|
+
def format_value value
|
63
|
+
CGI.escape(value.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def set_default_page_limit
|
68
|
+
@page_limit = 5
|
69
|
+
end
|
70
|
+
|
71
|
+
def default_criteria
|
72
|
+
@querystring = {:page => 1, :sort_type => POPULARITY,
|
73
|
+
:items_per_page => 50, :image_background_color => "FFFFFF",
|
74
|
+
:image_size => LARGE, :source => 'razsell', :opensearch => 1, :feed_type => "rss"}
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.attr_querystring_read(*args)
|
78
|
+
@querystring = {}
|
79
|
+
|
80
|
+
args.each do |attribute|
|
81
|
+
define_method "#{attribute}" do
|
82
|
+
@querystring[attribute]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.attr_querystring_write(*args)
|
88
|
+
@querystring = {}
|
89
|
+
|
90
|
+
args.each do |attribute|
|
91
|
+
define_method "#{attribute}=" do |value|
|
92
|
+
@querystring[attribute] = value
|
93
|
+
end\
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
attr_querystring_read :keywords, :product_line, :product_type,
|
100
|
+
:sort_type, :sort_period, :page, :items_per_page,
|
101
|
+
:feed_type, :image_size, :image_background_color,
|
102
|
+
:opensearch, :source
|
103
|
+
attr_querystring_write :keywords, :product_line, :product_type,
|
104
|
+
:sort_type, :sort_period, :image_size, :image_background_color
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
data/lib/razsell.rb
ADDED
data/lib/results.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
3
|
+
module Razsell
|
4
|
+
class Results
|
5
|
+
attr_accessor :items, :result_count, :items_per_page
|
6
|
+
|
7
|
+
def initialize feed
|
8
|
+
@start_index = 0
|
9
|
+
@items_per_page = 0
|
10
|
+
@result_count = 0
|
11
|
+
@items = []
|
12
|
+
|
13
|
+
populate_items feed
|
14
|
+
end
|
15
|
+
|
16
|
+
def add feed
|
17
|
+
populate_items feed
|
18
|
+
end
|
19
|
+
|
20
|
+
def item_count
|
21
|
+
@items.length
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_hash_from item
|
25
|
+
item_hash = {}
|
26
|
+
item_hash[:title] = strip_cdata(item.at("title").inner_html)
|
27
|
+
item_hash[:guid] = item.at("guid").inner_html
|
28
|
+
item_hash[:pub_date] = DateTime.parse(item.at("pubDate").inner_html)
|
29
|
+
item_hash[:link] = item.at("link").inner_html
|
30
|
+
item_hash[:author] = item.at("author").inner_html
|
31
|
+
item_hash[:description] = strip_cdata(item.at("media:description").inner_html)
|
32
|
+
item_hash[:thumbnail_url] = item.at("media:thumbnail")['url']
|
33
|
+
item_hash[:content_url] = item.at("media:content")['url']
|
34
|
+
item_hash[:keywords] = split_keywords(item.at("media:keywords").inner_html)
|
35
|
+
item_hash[:rating] = item.at("media:rating").inner_html
|
36
|
+
item_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def split_keywords words
|
40
|
+
return [] if (!words || words == "")
|
41
|
+
|
42
|
+
words.split(",").map { |s| s.strip }
|
43
|
+
end
|
44
|
+
|
45
|
+
def strip_cdata str
|
46
|
+
str[9..-4]
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_more_pages?
|
50
|
+
return false unless (@start_index && @items_per_page && @result_count)
|
51
|
+
(@start_index + @items_per_page) < @result_count
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def populate_items feed
|
56
|
+
doc = Hpricot::XML(feed)
|
57
|
+
rc = doc.at("opensearch:totalResults")
|
58
|
+
ipp = doc.at("opensearch:itemsPerPage")
|
59
|
+
si = doc.at("opensearch:startIndex")
|
60
|
+
|
61
|
+
@result_count = rc.inner_html.to_i if rc
|
62
|
+
@items_per_page = ipp.inner_html.to_i if ipp
|
63
|
+
@start_index = si.inner_html.to_i if si
|
64
|
+
|
65
|
+
doc.search("item").each do |item|
|
66
|
+
@items << Razsell::Item.new(build_hash_from(item))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/test/engine_test.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'engine'
|
3
|
+
require 'http_service'
|
4
|
+
|
5
|
+
module Razsell
|
6
|
+
class EngineTest < Test::Unit::TestCase
|
7
|
+
context "basic operations" do
|
8
|
+
should "initialize without parameters" do
|
9
|
+
engine = Razsell::Engine.new
|
10
|
+
end
|
11
|
+
|
12
|
+
should "accept http_service" do
|
13
|
+
http_service = Razsell::HttpService.new
|
14
|
+
http_service.expects(:get).once.returns("")
|
15
|
+
|
16
|
+
engine = Razsell::Engine.new :http_service => http_service
|
17
|
+
query = Razsell::Query.new
|
18
|
+
|
19
|
+
engine.request query
|
20
|
+
end
|
21
|
+
|
22
|
+
context "getting results" do
|
23
|
+
setup do
|
24
|
+
http_service = Razsell::HttpService.new
|
25
|
+
http_service.expects(:get).once.returns(feed("rockstar"))
|
26
|
+
|
27
|
+
engine = Razsell::Engine.new :http_service => http_service
|
28
|
+
query = Razsell::Query.new
|
29
|
+
|
30
|
+
@result = engine.request query
|
31
|
+
end
|
32
|
+
|
33
|
+
should "have total item count" do
|
34
|
+
assert_equal 3, @result.item_count
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "Getting multiple pages of results" do
|
39
|
+
setup do
|
40
|
+
http_service = Razsell::HttpService.new
|
41
|
+
http_service.expects(:get).twice.returns(feed("page_1"), feed("page_2"))
|
42
|
+
|
43
|
+
engine = Razsell::Engine.new :http_service => http_service
|
44
|
+
query = Razsell::Query.new
|
45
|
+
|
46
|
+
@result = engine.request query
|
47
|
+
end
|
48
|
+
|
49
|
+
should "have total item count" do
|
50
|
+
assert_equal 5, @result.item_count
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|