reviewed 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +18 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +8 -0
  5. data/LICENSE +22 -0
  6. data/README.md +36 -0
  7. data/Rakefile +9 -0
  8. data/lib/reviewed/article.rb +18 -0
  9. data/lib/reviewed/author.rb +4 -0
  10. data/lib/reviewed/base.rb +56 -0
  11. data/lib/reviewed/brand.rb +4 -0
  12. data/lib/reviewed/collection.rb +66 -0
  13. data/lib/reviewed/product.rb +14 -0
  14. data/lib/reviewed/request.rb +23 -0
  15. data/lib/reviewed/response.rb +12 -0
  16. data/lib/reviewed/util.rb +16 -0
  17. data/lib/reviewed/version.rb +4 -0
  18. data/lib/reviewed/website.rb +4 -0
  19. data/lib/reviewed.rb +52 -0
  20. data/reviewed.gemspec +26 -0
  21. data/spec/article_spec.rb +33 -0
  22. data/spec/author_spec.rb +4 -0
  23. data/spec/base_spec.rb +127 -0
  24. data/spec/brand_spec.rb +4 -0
  25. data/spec/collection_spec.rb +83 -0
  26. data/spec/fixtures/vcr/article/attachments.yml +281 -0
  27. data/spec/fixtures/vcr/article/find_page.yml +189 -0
  28. data/spec/fixtures/vcr/base/find_error_key.yml +239 -0
  29. data/spec/fixtures/vcr/base/find_ok.yml +310 -0
  30. data/spec/fixtures/vcr/base/where_collection.yml +1958 -0
  31. data/spec/fixtures/vcr/collection/products.yml +1416 -0
  32. data/spec/fixtures/vcr/product/attachments.yml +84 -0
  33. data/spec/fixtures/vcr/request/authors.yml +144 -0
  34. data/spec/fixtures/vcr/response/authors.yml +144 -0
  35. data/spec/product_spec.rb +23 -0
  36. data/spec/request_spec.rb +15 -0
  37. data/spec/response_spec.rb +26 -0
  38. data/spec/spec_helper.rb +25 -0
  39. data/spec/support/.gitkeep +0 -0
  40. data/spec/util_spec.rb +33 -0
  41. data/spec/website_spec.rb +4 -0
  42. metadata +221 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ tags
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in reviewed.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/reviewed/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Reviewed.com
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Reviewed
2
+
3
+ A Ruby Gem for Accessing the Reviewed.com API
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'reviewed'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install reviewed
18
+
19
+ ## Usage
20
+
21
+ First set your Reviewed.com API key:
22
+
23
+ Reviewed.api_key = 'my api key'
24
+
25
+ Now you should be able to query the service:
26
+
27
+ site = Reviewed::Website.find('DCI')
28
+ puts site.name
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+
@@ -0,0 +1,18 @@
1
+ module Reviewed
2
+ class Article < Base
3
+ def find_page(slug)
4
+ pages.find { |page| page.slug.match(/#{slug}/i) }
5
+ end
6
+
7
+ def attachments(tag=nil)
8
+ if tag.present?
9
+ @attributes[:attachments].select do |attachment|
10
+ attachment_tags = attachment.tags || []
11
+ attachment_tags.map(&:downcase).include?(tag.downcase)
12
+ end
13
+ else
14
+ @attributes[:attachments]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ module Reviewed
2
+ class Author < Base
3
+ end
4
+ end
@@ -0,0 +1,56 @@
1
+ module Reviewed
2
+ class Base
3
+ attr_accessor :raw_response, :resource_url
4
+
5
+ def initialize(attributes={}, raw_response=nil)
6
+ attributes.symbolize_keys!
7
+
8
+ unless attributes[:id].present?
9
+ raise ResourceError.new("Resource requires an ID")
10
+ end
11
+
12
+ @raw_response = raw_response
13
+ @attributes = Hashie::Mash.new(attributes)
14
+ end
15
+
16
+ def method_missing(sym, *args, &block)
17
+ if @attributes.has_key?(sym)
18
+ @attributes[sym]
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def self.find(id)
25
+ Reviewed.verify_key!
26
+
27
+ response = Reviewed::Request.get(resource_url(id))
28
+ new(response.json, response.raw)
29
+ end
30
+
31
+ def self.all
32
+ where({})
33
+ end
34
+
35
+ def self.where(options={})
36
+ Reviewed.verify_key!
37
+
38
+ response = Reviewed::Request.get(resource_url(nil, options))
39
+ Collection.new(self, response.json, options)
40
+ end
41
+
42
+ def self.resource_name=(value)
43
+ @resource_name = value
44
+ end
45
+
46
+ def self.resource_name
47
+ @resource_name ||= self.name.split('::').last.downcase
48
+ end
49
+
50
+ def self.resource_url(id=nil, options={})
51
+ url = [Reviewed.base_uri, API_VERSION, resource_name.pluralize, id].compact.join("/")
52
+ query_string = Util.build_query_string(options)
53
+ query_string.blank? ? url : "#{url}?#{query_string}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,4 @@
1
+ module Reviewed
2
+ class Brand < Base
3
+ end
4
+ end
@@ -0,0 +1,66 @@
1
+ module Reviewed
2
+ class Collection
3
+ include Enumerable
4
+ extend Forwardable
5
+ def_delegators :@items, :<<, :[], :[]=, :each, :first, :last, :length, :concat, :map, :collect, :empty?
6
+
7
+ attr_accessor :raw_response
8
+
9
+ def initialize(klass, json, options={})
10
+ page_data = json[:pagination].symbolize_keys!
11
+
12
+ @items = []
13
+ items = json[:data]
14
+ items.each do |item|
15
+ @items << klass.new(item)
16
+ end
17
+
18
+ @page_attributes = page_data
19
+ @raw_response = json
20
+ @request_options = options.symbolize_keys!
21
+ @klass = klass
22
+ end
23
+
24
+ def limit_value
25
+ per_page
26
+ end
27
+
28
+ def num_pages
29
+ total_pages
30
+ end
31
+
32
+ def total_count
33
+ total
34
+ end
35
+
36
+ def next_page
37
+ return nil if @page_attributes[:last_page]
38
+ fetch_page(@page_attributes[:next_page])
39
+ end
40
+
41
+ def previous_page
42
+ return nil if @page_attributes[:first_page]
43
+ fetch_page(@page_attributes[:previous_page])
44
+ end
45
+
46
+ def ==(other)
47
+ @raw_response == other.raw_response
48
+ end
49
+
50
+ def method_missing(sym, *args, &block)
51
+ clean_sym = sym.to_s.gsub(/\?/, '').to_sym
52
+ if @page_attributes.has_key?(clean_sym)
53
+ @page_attributes[clean_sym]
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def fetch_page(page=nil)
62
+ @request_options[:page] = page
63
+ @klass.where(@request_options)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ module Reviewed
2
+ class Product < Base
3
+ def attachments(tag=nil)
4
+ if tag.present?
5
+ @attributes[:attachments].select do |attachment|
6
+ attachment_tags = attachment.tags || []
7
+ attachment_tags.map(&:downcase).include?(tag.downcase)
8
+ end
9
+ else
10
+ @attributes[:attachments]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Reviewed
2
+ class Request
3
+
4
+ class << self
5
+
6
+ def get(url)
7
+ url = url =~ /http/ ? url : build_url(url)
8
+
9
+ begin
10
+ raw_response = RestClient.get(url, Util.build_request_headers)
11
+ Reviewed::Response.new(raw_response)
12
+ rescue RestClient::Exception => e
13
+ raise ResourceError.new(e.message)
14
+ end
15
+ end
16
+
17
+ def build_url(url)
18
+ url = [Reviewed.base_uri, API_VERSION, url].compact.join("/")
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,12 @@
1
+ module Reviewed
2
+ class Response
3
+
4
+ attr_accessor :raw
5
+ attr_accessor :json
6
+
7
+ def initialize(raw_response)
8
+ @raw = raw_response
9
+ @json = JSON.parse(raw_response).symbolize_keys!
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Reviewed
2
+ class Util
3
+ def self.build_request_headers(headers={})
4
+ headers['X-Reviewed-Authorization'] ||= Reviewed.api_key
5
+ headers['accept'] ||= 'json'
6
+ headers.stringify_keys!
7
+ end
8
+
9
+ def self.build_query_string(hash={})
10
+ hash.keys.inject('') do |query_string, key|
11
+ query_string << '&' unless key == hash.keys.first
12
+ query_string << "#{URI.encode(key.to_s)}=#{URI.encode(hash[key].to_s)}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ module Reviewed
2
+ VERSION = "0.0.1"
3
+ API_VERSION = 'v1'
4
+ end
@@ -0,0 +1,4 @@
1
+ module Reviewed
2
+ class Website < Base
3
+ end
4
+ end
data/lib/reviewed.rb ADDED
@@ -0,0 +1,52 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'active_support/core_ext'
4
+ require 'rest_client'
5
+ require 'hashie'
6
+ require 'forwardable'
7
+
8
+ require 'reviewed/version'
9
+ require 'reviewed/util'
10
+ require 'reviewed/base'
11
+ require 'reviewed/collection'
12
+ require 'reviewed/request'
13
+ require 'reviewed/response'
14
+
15
+ require 'reviewed/website'
16
+ require 'reviewed/product'
17
+ require 'reviewed/author'
18
+ require 'reviewed/brand'
19
+ require 'reviewed/article'
20
+
21
+ module Reviewed
22
+ class ConfigurationError < StandardError; end
23
+ class ResourceError < StandardError; end
24
+
25
+ @@config = {
26
+ base_uri: 'http://localhost:3000/api'
27
+ }
28
+
29
+ def self.api_key=(token)
30
+ @@config[:api_key] = token
31
+ end
32
+
33
+ def self.api_key
34
+ @@config[:api_key]
35
+ end
36
+
37
+ def self.base_uri=(uri)
38
+ @@config[:base_uri] = uri
39
+ end
40
+
41
+ def self.base_uri
42
+ @@config[:base_uri]
43
+ end
44
+
45
+ def self.verify_key!
46
+ if Reviewed.api_key.present?
47
+ true
48
+ else
49
+ raise ConfigurationError.new("Please set Reviewed.api_key before making a request")
50
+ end
51
+ end
52
+ end
data/reviewed.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/reviewed/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Nick Plante", "Kevin Incorvia"]
6
+ gem.email = ["development@reviewed.com"]
7
+ gem.description = %q{Client library for the Reviewed.com API}
8
+ gem.summary = %q{A Ruby Gem for Accessing the Reviewed.com API}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "reviewed"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Reviewed::VERSION
17
+
18
+ gem.add_dependency 'activesupport', '>= 3.0'
19
+ gem.add_dependency 'rest-client', '>= 1.6'
20
+ gem.add_dependency 'hashie', '~> 1.2'
21
+
22
+ gem.add_development_dependency 'rspec', '>= 2.10'
23
+ gem.add_development_dependency 'webmock', '>= 1.8'
24
+ gem.add_development_dependency 'vcr', '>= 2.1'
25
+ gem.add_development_dependency 'guard-rspec', '>= 1.0'
26
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Reviewed::Article do
4
+ describe 'find_page' do
5
+ use_vcr_cassette 'article/find_page'
6
+
7
+ it 'finds a page with a matching slug' do
8
+ article = Reviewed::Article.find('minden-master-ii-grill-review')
9
+ article.pages.length.should == 4
10
+ article.find_page('performance').should == article.pages[1]
11
+ end
12
+ end
13
+
14
+ describe 'attachments' do
15
+ use_vcr_cassette 'article/attachments'
16
+
17
+ before(:each) do
18
+ @article = Reviewed::Article.find('big-green-egg-medium-charcoal-grill-review')
19
+ end
20
+
21
+ it 'returns all attachments' do
22
+ @article.attachments.length.should > 1
23
+ end
24
+
25
+ it 'finds attachments by tag' do
26
+ attachments = @article.attachments('hero')
27
+ attachments.length.should == 1
28
+ attachments.each do |attachment|
29
+ attachment.tags.join(',').should match(/hero/i)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Reviewed::Author do
4
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ module Reviewed
4
+ class Example < Base
5
+ end
6
+ end
7
+
8
+ module Reviewed
9
+ class Article < Base
10
+ end
11
+ end
12
+
13
+ describe Reviewed::Base do
14
+ before(:each) do
15
+ Reviewed.api_key = TEST_KEY
16
+ end
17
+
18
+ describe 'initialization' do
19
+ it 'raises an error if a resource ID is not present' do
20
+ expect {
21
+ Reviewed::Example.new
22
+ }.to raise_error(Reviewed::ResourceError)
23
+ end
24
+
25
+ it 'returns a value' do
26
+ model = Reviewed::Example.new(id: 1234, name: 'Test')
27
+ model.id.should == 1234
28
+ end
29
+ end
30
+
31
+ describe 'find' do
32
+ before(:each) do
33
+ Reviewed::Example.resource_name = 'article' # test
34
+ end
35
+
36
+ it 'raises an error if the api key is not set' do
37
+ Reviewed.api_key = nil
38
+
39
+ expect {
40
+ Reviewed::Example.find('test')
41
+ }.to raise_error(Reviewed::ConfigurationError)
42
+ end
43
+
44
+ context 'with a valid request' do
45
+ use_vcr_cassette 'base/find_ok'
46
+
47
+ it 'fetches content from the api' do
48
+ model = Reviewed::Example.find('5036d7dd60de7d2065075752')
49
+ model.raw_response.should_not be_nil
50
+ end
51
+
52
+ it 'parses response json and returns an object' do
53
+ model = Reviewed::Example.find('5036d7dd60de7d2065075752')
54
+ model.class.should == Reviewed::Example
55
+ model.id.should == '5036d7dd60de7d2065075752'
56
+ model.name.should == 'Fast Appliances To Save You Time'
57
+ end
58
+ end
59
+
60
+ context 'with an invalid request' do
61
+ use_vcr_cassette 'base/find_error_key'
62
+
63
+ it 'complains if the api key is unauthorized' do
64
+ Reviewed.api_key = 'xxxxxxxxxxxxxxxx'
65
+
66
+ expect {
67
+ Reviewed::Example.find('50241b9c5da4ac8d38000001')
68
+ }.to raise_error(Reviewed::ResourceError, /unauthorized/i)
69
+ end
70
+
71
+ it 'complains if the requested resource is not found' do
72
+ expect {
73
+ Reviewed::Example.find('notfound')
74
+ }.to raise_error(Reviewed::ResourceError, /not found/i)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe 'where' do
80
+ use_vcr_cassette 'base/where_collection'
81
+
82
+ it 'returns a collection' do
83
+ collection = Reviewed::Article.all
84
+ collection.class.should == Reviewed::Collection
85
+ end
86
+
87
+ it 'returns the appropriate page of results' do
88
+ collection = Reviewed::Article.where(:page => 2)
89
+ collection.total.should == 7141
90
+ collection.current_page.should == 2
91
+ end
92
+
93
+ it 'filters collections using other supported options' do
94
+ collection = Reviewed::Article.where(:keywords => 'minden')
95
+ collection.total.should == 1
96
+ collection.last_page.should be_true
97
+ end
98
+
99
+ it 'returns an empty set if no matching data was found' do
100
+ collection = Reviewed::Article.where(:keywords => 'doesnotcompute')
101
+ collection.should be_empty
102
+ collection.total.should == 0
103
+ collection.out_of_bounds.should be_true
104
+ end
105
+ end
106
+
107
+ describe 'attributes' do
108
+ it 'returns the named attribute (via method missing)' do
109
+ model = Reviewed::Example.new(:id => 'id', :super_awesome => 'true')
110
+ model.super_awesome.should == 'true'
111
+ end
112
+ end
113
+
114
+ describe 'resource url' do
115
+ it 'returns an individual resource' do
116
+ Reviewed::Example.resource_url('article-123').should == 'https://the-guide-staging.herokuapp.com/api/v1/articles/article-123'
117
+ end
118
+
119
+ it 'returns a collection of resources' do
120
+ Reviewed::Example.resource_url(nil).should == 'https://the-guide-staging.herokuapp.com/api/v1/articles'
121
+ end
122
+
123
+ it 'includes a query parameter' do
124
+ Reviewed::Example.resource_url(nil, page: 2).should == 'https://the-guide-staging.herokuapp.com/api/v1/articles?page=2'
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Reviewed::Brand do
4
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ module Reviewed
4
+ class Product < Base
5
+ end
6
+ end
7
+
8
+ describe Reviewed::Collection do
9
+ use_vcr_cassette 'collection/products'
10
+
11
+ before(:each) do
12
+ Reviewed.api_key = TEST_KEY
13
+ @collection = Reviewed::Product.all # creates a collection
14
+ end
15
+
16
+ describe 'collection data' do
17
+ it 'is enumerable' do
18
+ @collection.each do |product|
19
+ product.class.should == Reviewed::Product
20
+ product.id.should_not be_blank
21
+ end
22
+ end
23
+
24
+ it 'contains the raw response' do
25
+ @collection.raw_response.should_not be_blank
26
+ end
27
+
28
+ it 'fetches the first page by default' do
29
+ @collection.current_page.should == 1
30
+ end
31
+ end
32
+
33
+ describe 'next page' do
34
+ it 'fetches the next page of results' do
35
+ page2 = @collection.next_page
36
+ page2.current_page.should == 2
37
+ end
38
+ end
39
+
40
+ describe 'previous page' do
41
+ it 'fetches the previous page of results' do
42
+ page2 = @collection.next_page
43
+ page1 = page2.previous_page
44
+ @collection.should == page1
45
+ page1.current_page.should == 1
46
+ end
47
+
48
+ it 'returns nil if there is no previous page' do
49
+ @collection.previous_page.should be_nil
50
+ end
51
+ end
52
+
53
+ describe 'page attributes (pagination)' do
54
+ it 'returns the total item count' do
55
+ @collection.total.should == 15060
56
+ end
57
+
58
+ it 'returns the total number of pages' do
59
+ @collection.total_pages.should == 753
60
+ end
61
+
62
+ it 'indicates whether this is the first or last page' do
63
+ @collection.first_page.should be_true
64
+ @collection.last_page.should be_false
65
+ end
66
+
67
+ it 'indicates if the page number is out of bounds' do
68
+ @collection.out_of_bounds.should be_false
69
+ end
70
+
71
+ it 'returns the offset' do
72
+ @collection.offset.should == 0
73
+ end
74
+
75
+ it 'returns the limit value (max per page)' do
76
+ @collection.per_page.should == 20
77
+ end
78
+
79
+ it 'returns the number of entries on the current page' do
80
+ @collection.entries_on_page.should == 20
81
+ end
82
+ end
83
+ end