razsell 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ tmp*
2
+ log*
3
+ *[~#]
4
+ optparse_example.rb
5
+ pkg/*
6
+ coverage*
7
+ nbproject*
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', '>= 0.8.1')
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.4
Binary file
@@ -0,0 +1,13 @@
1
+ Feature: Get Product Lines
2
+ In order to better organize my products
3
+ A user of razsell
4
+ Will ask for a list of product lines
5
+
6
+ Scenario: Getting all product lines for a user
7
+ Given the artist "KungFuTees" on Zazzle
8
+ When I ask for a list of the artists product lines
9
+ Then I should get 3 lines
10
+ And one should be named "Cheeze" with an id of "196854021783912655"
11
+ And one should be named "New Products" with an id of "196596454716712290"
12
+ And one should be named "Smim" with an id of "196983228798280961"
13
+
@@ -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,28 @@
1
+
2
+ Given /^the artist "([^\"]*)" on Zazzle$/ do |artist|
3
+ @artist = artist
4
+ @sut = RazsellMixedIn.new
5
+ end
6
+
7
+ When /^I ask for a list of the artists product lines$/ do
8
+ http_service = Razsell::HttpService.new
9
+ http_service.expects(:get).once.returns(feed("productlines"))
10
+
11
+ @product_lines = @sut.product_lines_for(@artist, :http_service => http_service)
12
+ end
13
+
14
+ Then /^I should get 3 lines$/ do
15
+ assert_equal 3, @product_lines.length
16
+ end
17
+
18
+ Then /^one should be named "([^\"]*)" with an id of "([^\"]*)"$/ do |name, id|
19
+ @found = false
20
+
21
+ @product_lines.each do |line|
22
+ if line.id == id && line.name == name
23
+ @found = true
24
+ end
25
+ end
26
+
27
+ assert @found
28
+ end
@@ -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,5 @@
1
+ require 'razsell'
2
+
3
+ class RazsellMixedIn
4
+ include Razsell
5
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Razsell
3
+ module ImageSizes
4
+ TINY = 'tiny'
5
+ SMALL = 'small'
6
+ MEDIUM = 'medium'
7
+ LARGE = 'large'
8
+ HUGE = 'huge'
9
+ end
10
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+
2
+ module Razsell
3
+ module SortMethods
4
+ POPULARITY = 'popularity'
5
+ DATE_CREATED = 'date_created'
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module Razsell
3
+ module SortPeriods
4
+ ALL_TIME = '0'
5
+ TODAY = '1'
6
+ THIS_WEEK = '7'
7
+ THIS_MONTH = '30'
8
+ end
9
+ end
data/lib/constants.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'constants/sort_methods'
2
+ require 'constants/image_sizes'
3
+ require 'constants/product_types'
4
+ require 'constants/sort_periods'
5
+
data/lib/engine.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'http_service'
2
+ require 'product_line_parser'
3
+
4
+ module Razsell
5
+ class Engine
6
+ def initialize opts={}
7
+ @http_service = get_http_service opts
8
+ end
9
+
10
+ def request query
11
+ feed = @http_service.get query
12
+ results = Razsell::Results.new feed
13
+
14
+ while results.has_more_pages? && query.advance_page
15
+ feed = @http_service.get query
16
+ results.add(feed)
17
+ end
18
+
19
+ results
20
+ end
21
+
22
+ def product_lines_for artist
23
+ url = "http://www.zazzle.com/#{artist}?src=razsell"
24
+
25
+ document = @http_service.get url
26
+
27
+ parser = ProductLineParser.new
28
+ parser.parse document
29
+ end
30
+
31
+ def get_http_service opts
32
+ opts[:http_service] ? opts[:http_service] : Razsell::HttpService.new
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ require 'open-uri'
2
+
3
+ module Razsell
4
+ class HttpService
5
+ def get query
6
+ if query.class == String
7
+ url = query
8
+ else
9
+ url = query.to_url
10
+ end
11
+
12
+ open(url)
13
+ end
14
+ end
15
+ end
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
@@ -0,0 +1,14 @@
1
+ module Razsell
2
+ class ProductLine
3
+ attr_accessor :id, :name
4
+
5
+ def initialize href, name
6
+ @id = strip_id(href)
7
+ @name = name
8
+ end
9
+
10
+ def strip_id href
11
+ href.split("cg=")[1]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'hpricot'
2
+ require 'product_line'
3
+
4
+ module Razsell
5
+ class ProductLineParser
6
+ def parse document
7
+ return [] unless document
8
+
9
+ elements = get_elements document
10
+ elements.map { |e| Razsell::ProductLine.new(e.attributes['href'] , e.inner_html) }
11
+ end
12
+
13
+ def get_elements document
14
+ doc = Hpricot(document)
15
+
16
+ (doc/"#page_module8/ul/li/a")
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
@@ -0,0 +1,17 @@
1
+ require 'query'
2
+ require 'results'
3
+ require 'item'
4
+ require 'engine'
5
+ require 'http_service'
6
+
7
+ module Razsell
8
+ def request query, opts={}
9
+ engine = Razsell::Engine.new opts
10
+ engine.request(query)
11
+ end
12
+
13
+ def product_lines_for artist, opts={}
14
+ engine = Razsell::Engine.new opts
15
+ engine.product_lines_for artist
16
+ end
17
+ end
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