kosher 0.0.3 → 0.1.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/.gitignore CHANGED
@@ -1,4 +1,7 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ .rvmrc
5
+ .rspec
4
6
  Gemfile.lock
7
+ credentials.yml
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in kosher.gemspec
4
3
  gemspec
data/LICENSE CHANGED
@@ -1,22 +1,13 @@
1
- (The MIT License)
2
-
3
- Copyright (c) 2010 Paper Cavalier
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 NONINFRINGEMENT.
19
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  Kosher
2
2
  ======
3
3
 
4
- ![Walter Benjamin](https://github.com/papercavalier/kosher/raw/master/walter_benjamin.jpg)
4
+ Kosher wraps Amazon in a loving embrace.
5
5
 
6
- description = Kosher.new "A withdrawn library copy"
7
- description.kosher?
8
- => false
6
+ ![Walter Benjamin](https://github.com/papercavalier/kosher/raw/master/walter_benjamin.jpg)
data/kosher.gemspec CHANGED
@@ -1,23 +1,31 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "kosher/version"
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'kosher/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "kosher"
6
+ s.name = 'kosher'
7
7
  s.version = Kosher::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Paper Cavalier"]
10
- s.email = ["code@papercvalier.com"]
11
- s.homepage = ""
12
- s.summary = %q{Kosher validates descriptions}
13
- s.description = %q{Have you ever wondered if you should buy a book?}
9
+ s.authors = ['Paper Cavalier']
10
+ s.email = 'code@papercavalier.com'
11
+ s.homepage = 'https://rubygems.org/gems/kosher'
12
+ s.summary = %q{Wraps Amazon in a loving embrace.}
13
+ s.description = %q{Kosher wraps Amazon in a loving embrace.}
14
14
 
15
- s.rubyforge_project = "kosher"
15
+ s.rubyforge_project = 'kosher'
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib"]
20
+ s.require_paths = ['lib']
21
21
 
22
- s.add_development_dependency("rspec", ["~> 2.4.0"])
22
+ s.add_dependency('sucker', '~> 1.3.1')
23
+ s.add_dependency('throttler', '~> 0.2.4')
24
+ s.add_development_dependency('fabrication', '~> 0.9.5')
25
+ s.add_development_dependency('rspec', '~> 2.5.0')
26
+ if RUBY_VERSION.include? '1.9'
27
+ s.add_development_dependency('ruby-debug19', '~> 0.11.6')
28
+ end
29
+ s.add_development_dependency('vcr', '~> 1.7.0')
30
+ s.add_development_dependency('webmock', '~> 1.6.2')
23
31
  end
@@ -0,0 +1,19 @@
1
+ module Kosher
2
+ class Algorithm
3
+ def initialize(response)
4
+ @response = response
5
+ end
6
+
7
+ def items
8
+ @response.map('Item') do |item|
9
+ Item.build(item)
10
+ end
11
+ end
12
+
13
+ def errors
14
+ @response.errors.map do |error|
15
+ error['Message'].scan(/[0-9A-Z]{10}/).first rescue nil
16
+ end.compact
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Kosher
2
+ class Condition < Struct.new(:in_words)
3
+ def to_i
4
+ case in_words
5
+ when 'new'
6
+ 1
7
+ when 'mint'
8
+ 2
9
+ when 'verygood'
10
+ 3
11
+ when 'good'
12
+ 4
13
+ when 'acceptable'
14
+ 5
15
+ else
16
+ 6
17
+ end
18
+ end
19
+
20
+ def kosher?
21
+ to_i <= 4
22
+ end
23
+
24
+ def new?
25
+ in_words == 'new'
26
+ end
27
+
28
+ def used?
29
+ !new?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,59 @@
1
+ module Kosher
2
+ class Description < String
3
+ DAMAGED = "\\b(?:missing|torn|broken|split|discard|withdrawn|rent|stain|school|damaged|water)"
4
+ EXLIB = "(?:e?x|discarded|retired|former|has|have)[\\s._-]*lib"
5
+ MARKED = "(highlight|hilit|underlin)"
6
+ MISSING_VOL = "(vols?|volume) only"
7
+ REVIEW_COPY = "\\b(?:uncorrected|advanced?\\sreview|arc)\\b"
8
+
9
+ def kosher?
10
+ !(present? && bad?)
11
+ end
12
+
13
+ private
14
+
15
+ def bad?
16
+ damaged? ||
17
+ ex_library? ||
18
+ marked? ||
19
+ missing_volume? ||
20
+ review_copy?
21
+ end
22
+
23
+ def damaged?
24
+ matches(DAMAGED)
25
+ end
26
+
27
+ def does_not_match(value)
28
+ !match(Regexp.new(value, true))
29
+ end
30
+
31
+ def ex_library?
32
+ matches(EXLIB) && does_not_match(negation_of(EXLIB))
33
+ end
34
+
35
+ def marked?
36
+ matches(MARKED) && does_not_match(negation_of(MARKED))
37
+ end
38
+
39
+ def matches(value)
40
+ !does_not_match(value)
41
+ end
42
+
43
+ def missing_volume?
44
+ matches(MISSING_VOL)
45
+ end
46
+
47
+ def negation_of(value)
48
+ "(?:no|not an?)\\s+#{value}"
49
+ end
50
+
51
+ def present?
52
+ self != ''
53
+ end
54
+
55
+ def review_copy?
56
+ matches(REVIEW_COPY)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ module Kosher
2
+ class Item < Struct.new(:asin, :offers)
3
+
4
+ def self.build(doc)
5
+ asin = doc['ASIN']
6
+ offers = build_offers(doc['Offers']['Offer'])
7
+
8
+ new(asin, offers)
9
+ end
10
+
11
+ private
12
+
13
+ def self.build_offers(offers)
14
+ [offers].flatten.compact.map do |offer|
15
+
16
+ # Senify Yen because Ruby Money says so
17
+ price = offer['OfferListing']['Price']
18
+ if price['CurrencyCode'] == 'JPY'
19
+ price['Amount'] = price['Amount'].to_i * 100
20
+ end
21
+
22
+ Offer.build(offer)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ module Kosher
2
+ class Offer < Struct.new(
3
+ :seller,
4
+ :condition,
5
+ :description,
6
+ :ships_in,
7
+ :ships_free,
8
+ :cents)
9
+
10
+ def self.build(doc)
11
+ offer = new
12
+ offer.seller = Seller.build(doc['Merchant'])
13
+
14
+ attributes = doc['OfferAttributes']
15
+ offer.condition = Condition.new(attributes['SubCondition'])
16
+ offer.description = Description.new(attributes['ConditionNote'].to_s)
17
+
18
+ listing = doc['OfferListing']
19
+ offer.ships_in = listing['AvailabilityAttributes']['MaximumHours'].to_i
20
+ offer.ships_free = listing['IsEligibleForSuperSaverShipping'] == '1'
21
+ offer.cents = listing['Price']['Amount'].to_i
22
+
23
+ offer
24
+ end
25
+
26
+ def kosher?
27
+ condition.kosher? && seller.kosher? && description.kosher? && ships_now?
28
+ end
29
+
30
+ def ships_now?
31
+ ships_in.to_i <= 48
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ module Kosher
2
+ class Request < Sucker::Request
3
+ def initialize(args={})
4
+ super
5
+ add_parameters
6
+ end
7
+
8
+ def batchify(asins)
9
+ self.<<({ 'ItemLookup.1.ItemId' => asins[0, 10] })
10
+ self.<<({ 'ItemLookup.2.ItemId' => asins[10, 10] }) if asins.size > 10
11
+ end
12
+
13
+ def get
14
+ Algorithm.new(super)
15
+ end
16
+
17
+ private
18
+
19
+ def add_parameters
20
+ self.<<({
21
+ 'Operation' => 'ItemLookup',
22
+ 'ItemLookup.Shared.IdType' => 'ASIN',
23
+ 'ItemLookup.Shared.Condition' => 'All',
24
+ 'ItemLookup.Shared.MerchantId' => 'All',
25
+ 'ItemLookup.Shared.ResponseGroup' => ['OfferFull', 'SalesRank'] })
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Kosher
2
+ class Seller < Struct.new(:merchant_id, :average_rating)
3
+ class << self
4
+ attr_accessor :blacklist
5
+
6
+ def build(doc)
7
+ merchant_id = doc['MerchantId']
8
+ average_rating = doc['AverageFeedbackRating'].to_f
9
+
10
+ new(merchant_id, average_rating)
11
+ end
12
+ end
13
+
14
+ def blacklist
15
+ self.class.blacklist
16
+ end
17
+
18
+ def blacklisted?
19
+ blacklist.include? merchant_id rescue false
20
+ end
21
+
22
+ def kosher?
23
+ return false if blacklisted?
24
+
25
+ average_rating == 0.0 || average_rating > 4.7
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Kosher
2
- VERSION = "0.0.3"
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/kosher.rb CHANGED
@@ -1,9 +1,14 @@
1
- require "kosher/string"
2
- require "kosher/regexps"
1
+ require 'sucker'
2
+ require 'kosher/algorithm'
3
+ require 'kosher/condition'
4
+ require 'kosher/description'
5
+ require 'kosher/item'
6
+ require 'kosher/offer'
7
+ require 'kosher/request'
8
+ require 'kosher/seller'
3
9
 
4
10
  module Kosher
5
- def self.new(val)
6
- String.new(val)
11
+ def self.new(args={})
12
+ Request.new(args)
7
13
  end
8
14
  end
9
-
@@ -0,0 +1,7 @@
1
+ Fabricator(:kosher_condition, :class_name => 'kosher/condition') do
2
+ in_words "new"
3
+ end
4
+
5
+ Fabricator(:unkosher_condition, :class_name => 'kosher/condition') do
6
+ in_words "acceptable"
7
+ end
@@ -0,0 +1,8 @@
1
+ Fabricator(:offer, :class_name => 'kosher/offer') do
2
+ seller { Fabricate(:good_seller) }
3
+ description Kosher::Description.new('')
4
+ condition { Fabricate(:kosher_condition) }
5
+ ships_free false
6
+ ships_in 48
7
+ cents 100
8
+ end
@@ -0,0 +1,15 @@
1
+ Fabricator(:seller, :class_name => 'kosher/seller') do
2
+ merchant_id Faker::Amazon.merchant_id
3
+ end
4
+
5
+ Fabricator(:new_seller, :from => :seller) do
6
+ average_rating 0.0
7
+ end
8
+
9
+ Fabricator(:good_seller, :from => :seller) do
10
+ average_rating 5.0
11
+ end
12
+
13
+ Fabricator(:bad_seller, :from => :seller) do
14
+ average_rating 4.5
15
+ end
@@ -0,0 +1,112 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://ecs.amazonaws.com:80/onca/xml?AWSAccessKeyId=0ZVSQ33MDFPQS8H2PM02&AssociateTag=papecava-20&ItemLookup.1.ItemId=0143105825&ItemLookup.Shared.Condition=All&ItemLookup.Shared.IdType=ASIN&ItemLookup.Shared.MerchantId=All&ItemLookup.Shared.ResponseGroup=OfferFull,SalesRank&Operation=ItemLookup&Service=AWSECommerceService&Signature=x3dZXO/H7AvZ7x/bt6P1iTAx13QatyVbMyc1v9yKtzc=&Timestamp=2011-03-02T19:25:56Z&Version=2010-11-01
6
+ body:
7
+ headers:
8
+ accept-encoding:
9
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
10
+ response: !ruby/struct:VCR::Response
11
+ status: !ruby/struct:VCR::ResponseStatus
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ date:
16
+ - Wed, 02 Mar 2011 19:25:56 GMT
17
+ server:
18
+ - Server
19
+ content-type:
20
+ - text/xml;charset=UTF-8
21
+ vary:
22
+ - Accept-Encoding,User-Agent
23
+ content-encoding:
24
+ - gzip
25
+ content-length:
26
+ - "3620"
27
+ nncoection:
28
+ - close
29
+ body: !binary |
30
+ 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
110
+ tnT+E+jS5/89Q+XfeGWzi8lBAAA=
111
+
112
+ http_version: "1.1"