power_reviews 0.1.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README +67 -7
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/generators/power_reviews/templates/config/power_reviews.yml +6 -4
- data/generators/power_reviews/templates/lib/tasks/power_reviews_tasks.rake +4 -10
- data/lib/power_reviews/config.rb +22 -11
- data/lib/power_reviews/feed.rb +3 -2
- data/lib/power_reviews/protocols/base.rb +3 -0
- data/lib/power_reviews/protocols/cp.rb +4 -0
- data/lib/power_reviews/protocols/ftp.rb +5 -0
- data/lib/power_reviews/review_data.rb +100 -0
- data/lib/power_reviews/sync.rb +34 -18
- data/lib/power_reviews/view_helpers.rb +88 -13
- data/lib/power_reviews.rb +1 -0
- data/power_reviews.gemspec +55 -46
- data/test/fixtures/rawdata/review_data_complete.xml +81 -0
- data/test/fixtures/rawdata/review_data_complete.xsd +344 -0
- data/test/fixtures/rawdata/review_data_summary.xml +1967 -0
- data/test/fixtures/rawdata/review_data_summary.xsd +161 -0
- data/test/power_reviews/config_test.rb +21 -0
- data/test/power_reviews/review_data_test.rb +41 -0
- data/test/power_reviews/view_helpers_test.rb +26 -0
- data/test/power_reviews_test.rb +0 -1
- metadata +85 -53
- data/.gitignore +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b2a2e183b2e2a588ad0b7f13a74d761b3d4f5583
|
4
|
+
data.tar.gz: dd366a763d0c19fe4bd17e5eaeeb1aef0b0d3789
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6bbbedf27521de7be69d7bb136477ea5535a4d38c2c848eab439ee84dd2f083b256838c3a517223325e07ef328af649802783daec374b0f08d2a6a4eb9515c23
|
7
|
+
data.tar.gz: de26c90d3c0cdcc72d339421a71df29a2ea8b7ed8332dfea6656ce45231a1dd6d00d655ff76def731678ff04a6e56e65234230dd657360a1a94f30f5c29534ae
|
data/README
CHANGED
@@ -1,13 +1,73 @@
|
|
1
|
-
PowerReviews
|
2
|
-
============
|
1
|
+
= PowerReviews
|
3
2
|
|
4
|
-
|
3
|
+
Integrates the third party PowerReviews service with Ruby. Currently focused on Ruby on Rails
|
4
|
+
but includes some tools to aid in non-rails tasks like data feed generation and automated
|
5
|
+
zip downloads/uploads.
|
5
6
|
|
7
|
+
== Dependencies
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
PowerReviews gem requires FasterCSV[http://fastercsv.rubyforge.org/] for csv generation and
|
10
|
+
Whenever[http://www.github.com/javan/whenever] for managing your crontab. It also requires that
|
11
|
+
a native +zip+ tool be installed.
|
9
12
|
|
10
|
-
|
13
|
+
which zip
|
14
|
+
# /usr/bin/zip
|
11
15
|
|
16
|
+
== Getting Started
|
12
17
|
|
13
|
-
|
18
|
+
Step one, install the gem.
|
19
|
+
|
20
|
+
In your rails environment file:
|
21
|
+
|
22
|
+
config.gem 'power_reviews', :source => 'http://gems.k2sportsdev.com'
|
23
|
+
|
24
|
+
Then
|
25
|
+
|
26
|
+
rake gems:install
|
27
|
+
|
28
|
+
or from your shell:
|
29
|
+
|
30
|
+
gem install power_reviews --source=http://gems.k2sportsdev.com
|
31
|
+
|
32
|
+
With the gem installed it's now time to generate the necessary files in your rails project.
|
33
|
+
From the root of your rails project run:
|
34
|
+
|
35
|
+
script/generate power_reviews
|
36
|
+
|
37
|
+
Which will output:
|
38
|
+
|
39
|
+
exists lib/tasks
|
40
|
+
create lib/tasks/power_reviews_tasks.rake
|
41
|
+
exists config/initializers
|
42
|
+
create config/power_reviews.yml
|
43
|
+
create config/initializers/power_reviews.rb
|
44
|
+
create config/schedule.rb
|
45
|
+
|
46
|
+
=== Power Reviews configuration
|
47
|
+
|
48
|
+
Define which files the PowerReviews::Sync class will be looking for when doing its nigtly download
|
49
|
+
of files, like the database.yml file in your rails configuration you can have a different configuration
|
50
|
+
for each environment. In this example, for development PowerReviews will use the local filesystem instead
|
51
|
+
of connection to the ftp server with the live data.
|
52
|
+
|
53
|
+
[+:protocol+] is the type of sync client you wish to use +:cp+ or +:ftp+
|
54
|
+
[+:zip+l] the zip file you will receive from Power Reviews
|
55
|
+
|
56
|
+
development:
|
57
|
+
protocol: cp
|
58
|
+
zip: test/brand.zip
|
59
|
+
done: test/brand_done.txt
|
60
|
+
datafeed: tmp/brand_feed.zip
|
61
|
+
|
62
|
+
production:
|
63
|
+
protocol: ftp
|
64
|
+
zip: brand.zip
|
65
|
+
done: brand_done.txt
|
66
|
+
data_feed: brand_data.zip
|
67
|
+
host: partners.powerreviews.com
|
68
|
+
username: user
|
69
|
+
password: passy
|
70
|
+
|
71
|
+
== Example
|
72
|
+
|
73
|
+
Copyright (c) 2009 K2 Sports, released under the MIT license
|
data/Rakefile
CHANGED
@@ -12,11 +12,13 @@ begin
|
|
12
12
|
gemspec.email = "beaucollins@gmail.com"
|
13
13
|
gemspec.homepage = "http://github.com/beaucollins/power_reviews"
|
14
14
|
gemspec.authors = ["Beau Collins"]
|
15
|
-
gemspec.add_runtime_dependency '
|
15
|
+
gemspec.add_runtime_dependency 'whenever'
|
16
16
|
gemspec.add_runtime_dependency 'fastercsv'
|
17
|
+
gemspec.add_development_dependency 'activesupport'
|
18
|
+
gemspec.add_development_dependency 'shoulda'
|
17
19
|
end
|
18
20
|
rescue LoadError
|
19
|
-
puts "Jeweler not available. Install it with:
|
21
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
20
22
|
end
|
21
23
|
|
22
24
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.2
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# For retrieving the zip file from PowerReviews
|
1
|
+
# For retrieving the zip file from PowerReviews and delivering your updated data feed
|
2
2
|
#
|
3
3
|
#
|
4
4
|
|
@@ -6,11 +6,13 @@ development:
|
|
6
6
|
protocol: cp
|
7
7
|
zip: test/k2skis.zip
|
8
8
|
done: test/k2skis_done.txt
|
9
|
+
datafeed: test/k2skis_data.zip
|
9
10
|
|
10
11
|
production:
|
11
12
|
protocol: ftp
|
12
13
|
zip: k2skis.zip
|
13
14
|
done: k2skis_done.txt
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
data_feed: k2skis_data.zip
|
16
|
+
host: partners.powerreviews.com
|
17
|
+
username: user
|
18
|
+
password: passy
|
@@ -1,19 +1,13 @@
|
|
1
|
-
# desc "Explaining what the task does"
|
2
|
-
# task :power_reviews do
|
3
|
-
# # Task goes here
|
4
|
-
# end
|
5
1
|
namespace :power_reviews do
|
6
2
|
|
7
|
-
desc "
|
3
|
+
desc "Outputs the PowerReviews feed in CSV format"
|
8
4
|
task :feed => :environment do
|
9
|
-
puts
|
10
|
-
PowerReviews::Feed.process
|
11
|
-
puts "Completed"
|
5
|
+
puts PowerReviews::Feed.process
|
12
6
|
end
|
13
7
|
|
14
|
-
desc "Copy the zip file from the ftp and unpack it"
|
8
|
+
desc "Copy the zip file from the ftp and unpack it then deliver the product feed"
|
15
9
|
task :sync => :environment do
|
16
|
-
puts "
|
10
|
+
puts "Syncing PowerReviews data"
|
17
11
|
PowerReviews::Sync.execute
|
18
12
|
puts "Completed"
|
19
13
|
end
|
data/lib/power_reviews/config.rb
CHANGED
@@ -1,20 +1,31 @@
|
|
1
1
|
module PowerReviews
|
2
2
|
class Config
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class << self
|
5
|
+
def merchant_group_id
|
6
|
+
@merchant_group_id
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def merchant_group_id=id
|
10
|
+
@merchant_group_id = id
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def site_id
|
14
|
+
@site_id
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
def site_id=id
|
18
|
+
@site_id = id
|
19
|
+
end
|
20
|
+
|
21
|
+
def review_data_path
|
22
|
+
@review_data_path || 'public/system/pwr'
|
23
|
+
end
|
24
|
+
|
25
|
+
def review_data_path=path
|
26
|
+
@review_data_path = path
|
27
|
+
end
|
28
|
+
|
18
29
|
end
|
19
30
|
|
20
31
|
end
|
data/lib/power_reviews/feed.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "faster_csv"
|
2
|
+
|
1
3
|
module PowerReviews
|
2
4
|
|
3
5
|
# Collects the feed data then outputs to CSV file for delivery to the power review peeps
|
@@ -28,8 +30,7 @@ module PowerReviews
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def to_csv
|
31
|
-
|
32
|
-
FasterCSV.open("#{RAILS_ROOT}/tmp/power_reviews.csv", 'w') do |csv|
|
33
|
+
@csv ||= FasterCSV.generate do |csv|
|
33
34
|
csv << FeedItem.field_names
|
34
35
|
@items.each do |item|
|
35
36
|
csv << item.values
|
@@ -20,6 +20,11 @@ module PowerReviews
|
|
20
20
|
client.getbinaryfile(@config['zip'], to)
|
21
21
|
end
|
22
22
|
|
23
|
+
# Puts the data_feed on the specified ftp server
|
24
|
+
def copy_data_feed(from)
|
25
|
+
client.putbinaryfile(from, @config['data_feed'])
|
26
|
+
end
|
27
|
+
|
23
28
|
# do any cleanup necessary
|
24
29
|
def cleanup
|
25
30
|
@client.close
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
module PowerReviews
|
5
|
+
|
6
|
+
class ReviewData
|
7
|
+
|
8
|
+
class MissingData < StandardError; end;
|
9
|
+
|
10
|
+
DATA_FILES = ['review_data_complete', 'review_data_summary']
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
# retrieve the summary data for the given page_id
|
15
|
+
def summary(page_id)
|
16
|
+
document.summary(page_id)
|
17
|
+
rescue MissingData
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload!
|
22
|
+
@document = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def document
|
28
|
+
@document ||= new
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# initializes a new ReviewData document
|
35
|
+
def initialize(search_path=nil)
|
36
|
+
@search_path = search_path || PowerReviews::Config.review_data_path
|
37
|
+
end
|
38
|
+
|
39
|
+
def summary(page_id)
|
40
|
+
node = nil
|
41
|
+
summary_data.elements.each("//pageid") do |e|
|
42
|
+
node = e.parent if e.get_text == page_id
|
43
|
+
end
|
44
|
+
!node.nil? ? Summary.new(node) : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def summary_data
|
48
|
+
@summary_data ||= read_document('review_data_summary')
|
49
|
+
end
|
50
|
+
|
51
|
+
def product_data
|
52
|
+
@product_data ||= read_document('review_data_complete')
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def read_document(name)
|
58
|
+
REXML::Document.new(File.new(xml_documents[name]))
|
59
|
+
end
|
60
|
+
|
61
|
+
def xml_documents
|
62
|
+
return @files unless @files.nil?
|
63
|
+
files = {}
|
64
|
+
Find.find(@search_path) do |path|
|
65
|
+
bn = File.basename(path, '.xml')
|
66
|
+
files[bn] = path if DATA_FILES.include?(bn)
|
67
|
+
end
|
68
|
+
missing = DATA_FILES.select { |f| files[f].blank? }
|
69
|
+
raise MissingData, "missing Power Reviews xml: #{missing.collect {|f| f + ".xml"}.join(', ')}" unless missing.empty?
|
70
|
+
@files = files
|
71
|
+
@files
|
72
|
+
end
|
73
|
+
|
74
|
+
class Summary
|
75
|
+
|
76
|
+
attr_reader :full_name, :average_rating, :reviews
|
77
|
+
|
78
|
+
def initialize(element)
|
79
|
+
@element = element
|
80
|
+
@full_name = text_for_element('name')
|
81
|
+
@average_rating = text_for_element('average_rating_decimal').to_s.to_f
|
82
|
+
@reviews = text_for_element('fullreviews').to_s.to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def text_for_element(name)
|
88
|
+
element = nil
|
89
|
+
@element.each_element(name) { |e| element = e if element.nil? }
|
90
|
+
element.nil? ? '' : element.get_text
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|
data/lib/power_reviews/sync.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'power_reviews/protocols/base'
|
2
2
|
require 'power_reviews/protocols/cp'
|
3
3
|
require 'power_reviews/protocols/ftp'
|
4
|
+
require 'power_reviews/feed'
|
4
5
|
|
5
6
|
module PowerReviews
|
6
7
|
# Downloads the ftp file and stores it in the correct location unzipped
|
@@ -13,29 +14,44 @@ module PowerReviews
|
|
13
14
|
:ftp => Protocols::Ftp
|
14
15
|
}
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Execute the necessary actions
|
20
|
+
def execute
|
21
|
+
self.start do |client, config|
|
19
22
|
|
20
|
-
|
23
|
+
zip_path = "#{Rails.root}/public/system/reviews.zip"
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
unless client.done?
|
26
|
+
FileUtils.mkdir_p(File.dirname(zip_path))
|
27
|
+
# we need the correct path to be setup
|
28
|
+
client.copy_zip(zip_path)
|
29
|
+
`cd #{File.dirname(zip_path)} && unzip -o #{zip_path}`
|
30
|
+
client.done!
|
31
|
+
end
|
29
32
|
|
33
|
+
# zip up the powerreviews data tell the client to store it
|
34
|
+
data_path = "#{RAILS_ROOT}/tmp/review_data.csv"
|
35
|
+
File.open(data_path, 'w') do |f|
|
36
|
+
f.puts PowerReviews::Feed.process
|
37
|
+
end
|
38
|
+
client.copy_data_feed(data_path)
|
39
|
+
|
40
|
+
end
|
30
41
|
end
|
31
|
-
end
|
32
42
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
# Given the environment loads up the yaml file and creates a new instance
|
44
|
+
def start
|
45
|
+
config = self.configure
|
46
|
+
syncer = PROTOCOLS[config.delete('protocol').intern].new(config)
|
47
|
+
yield syncer, config
|
48
|
+
syncer.cleanup
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure
|
52
|
+
@config ||= YAML::load(File.open("#{RAILS_ROOT}/config/power_reviews.yml"))[Rails.env] || {}
|
53
|
+
end
|
54
|
+
|
39
55
|
end
|
40
56
|
|
41
57
|
end
|
@@ -1,10 +1,51 @@
|
|
1
1
|
module PowerReviews
|
2
2
|
module ViewHelpers
|
3
|
+
|
4
|
+
# generates hReview compatible HTML for the given page_id
|
5
|
+
# renders the content as HTML at render time instead of relying
|
6
|
+
# on the Power Reviews javascript library to render the review content
|
7
|
+
def power_reviews_hreview(page_id, &blk)
|
8
|
+
|
9
|
+
summary = PowerReviews::ReviewData.summary(page_id)
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
yield summary unless summary.nil?
|
13
|
+
elsif !summary.nil?
|
3
14
|
|
4
|
-
|
5
|
-
|
15
|
+
<<-review
|
16
|
+
<div class='hreview-aggregate'>
|
17
|
+
<span class="item">
|
18
|
+
<span class="fn">#{summary.full_name}</span>
|
19
|
+
</span>
|
20
|
+
<span class="rating">
|
21
|
+
<span class="average">#{summary.average_rating}</span>
|
22
|
+
</span>
|
23
|
+
based on
|
24
|
+
<span class="count">#{summary.reviews}</span> user reviews.
|
25
|
+
</div>
|
26
|
+
review
|
27
|
+
else
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Includes the power reviews javascript, optionally provide the path to
|
33
|
+
# the javascript file if not installed in the default location
|
34
|
+
def power_reviews_javascript_tag(path_to_file = '/system/pwr/engine/js/full')
|
35
|
+
unless @power_reviews_javascript_needed.nil?
|
36
|
+
javascript_include_tag(path_to_file)
|
37
|
+
end
|
6
38
|
end
|
7
39
|
|
40
|
+
# Generates the javascript variables for a page that will be rendering
|
41
|
+
# PowerReviews elements
|
42
|
+
#
|
43
|
+
# Options:
|
44
|
+
#
|
45
|
+
# +:locale+ - default: en_US
|
46
|
+
# +:zip_location+ - where Power Reviews data is stored default: /system/
|
47
|
+
# +:pr_style_sheet - Location of custom Power Reviews stylesheet default: "" (empty string)
|
48
|
+
#
|
8
49
|
def power_reviews_javascript_variables(options = {})
|
9
50
|
options = {
|
10
51
|
:locale => 'en_US',
|
@@ -15,31 +56,65 @@ module PowerReviews
|
|
15
56
|
options.collect { |key, val| "var pr_#{key} = '#{val}';" unless val.blank? }.compact!.join("\n")
|
16
57
|
end
|
17
58
|
end
|
18
|
-
|
59
|
+
|
60
|
+
# Generates JavaScript to display the Power Reviews "snippet" element. Will show a summary
|
61
|
+
# of the reviews for the given +page_id+.
|
62
|
+
#
|
63
|
+
# Arguments:
|
64
|
+
# +page_id+ - the identifier for the product
|
65
|
+
# +write_review_url+ - the url to link to a page where the user can author a review
|
66
|
+
# +review_ancor+ - the url or anchor that links to the full reviews. Default: "#ReviewHeader"
|
19
67
|
def power_reviews_snippet(page_id, write_review_url, review_anchor = '#ReviewHeader')
|
68
|
+
@power_reviews_javascript_needed = true
|
20
69
|
javascript_tag do
|
21
70
|
<<-eos
|
22
|
-
POWERREVIEWS
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
71
|
+
if(POWERREVIEWS){
|
72
|
+
POWERREVIEWS.display.snippet(document, {
|
73
|
+
pr_page_id : '#{page_id}',
|
74
|
+
pr_write_review : '#{write_review_url}',
|
75
|
+
pr_read_review : '#{review_anchor}'
|
76
|
+
});
|
77
|
+
}
|
27
78
|
eos
|
28
79
|
end
|
29
80
|
end
|
30
|
-
|
81
|
+
|
82
|
+
# Displays the full reviews for the given +page_id+
|
83
|
+
#
|
84
|
+
# Arguments:
|
85
|
+
# +page_id+ - The identifier for the item to show reviews for
|
86
|
+
# +write_review_url+ - A url for a page where the user can write a review for the given product
|
31
87
|
def power_reviews_display(page_id, write_review_url)
|
88
|
+
@power_reviews_javascript_needed = true
|
32
89
|
javascript_tag do
|
33
90
|
<<-eos
|
34
|
-
POWERREVIEWS
|
35
|
-
|
36
|
-
|
37
|
-
|
91
|
+
if(POWERREVIEWS){
|
92
|
+
POWERREVIEWS.display.engine(document, {
|
93
|
+
pr_page_id : '#{page_id}',
|
94
|
+
pr_write_review : '#{write_review_url}'
|
95
|
+
});
|
96
|
+
}
|
38
97
|
eos
|
39
98
|
end
|
40
99
|
end
|
41
100
|
|
101
|
+
# Generates the javascript that renders the PowerReviews authoring form.
|
102
|
+
#
|
103
|
+
# Arguments:
|
104
|
+
# +page_id+ - The id of the item being reviewd
|
105
|
+
#
|
106
|
+
# Options:
|
107
|
+
# +:locale+ default ""
|
108
|
+
# +:site_id+ default: PowerReviews::Config.site_id
|
109
|
+
# +:merchant_group_id+ default: PowerReviews::Config.merchant_group_id
|
110
|
+
# +:source+ default: 'web'
|
111
|
+
# +:merchant_user_id+ default: ''
|
112
|
+
# +:merchant_user_email+ default: ''
|
113
|
+
# +:promo_code+ default: ''
|
114
|
+
# +:style_sheet+ default: ''
|
115
|
+
|
42
116
|
def power_reviews_form(page_id, options = {})
|
117
|
+
@power_reviews_javascript_needed = true
|
43
118
|
options = {
|
44
119
|
:locale => '',
|
45
120
|
:site_id => PowerReviews::Config.site_id,
|