kosher 0.2.24 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,2 @@
1
1
  source 'http://rubygems.org'
2
-
3
- gem 'rspec', '~> 2.5.0'
4
- gem 'ruby-debug19', '~> 0.11.6'
5
-
6
2
  gemspec
data/kosher.gemspec CHANGED
@@ -14,7 +14,19 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = 'kosher'
16
16
 
17
- s.add_dependency('money', '~> 3.6.1')
17
+ {
18
+ 'rspec' => '~> 2.6.0',
19
+ 'ruby-debug19' => '~> 0.11.6'
20
+ }.each do |lib, version|
21
+ s.add_development_dependency lib, version
22
+ end
23
+
24
+ {
25
+ 'money' => '~> 3.6.1',
26
+ 'structure' => '~> 0.5.0'
27
+ }.each do |lib, version|
28
+ s.add_runtime_dependency lib, version
29
+ end
18
30
 
19
31
  s.files = `git ls-files`.split("\n")
20
32
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -1,11 +1,22 @@
1
1
  module Kosher
2
- class Availability < Struct.new(:hours)
2
+
3
+ # The availability of an offer.
4
+ #
5
+ # This represents within how many hours an offer is expected to ship out.
6
+ class Availability < Structure
3
7
  include Threshold
4
8
 
5
- self.threshold = 48
9
+ key :hours, :type => Integer
6
10
 
11
+ # Returns whether the availability is kosher.
12
+ #
13
+ # An availability is kosher if the item ships within a maximum threshold of
14
+ # hours.
15
+ #
16
+ # If we do not know when an item will ship (e.g. Amazon's preorders), we
17
+ # should set hours to nil, which will render the availability unkosher.
7
18
  def kosher?
8
- hours <= threshold
19
+ !hours.nil? && hours <= threshold
9
20
  end
10
21
  end
11
22
  end
data/lib/kosher/book.rb CHANGED
@@ -1,10 +1,17 @@
1
1
  module Kosher
2
- class Book < Struct.new(:venue, :isbn, :asin, :offers_total, :offers)
3
2
 
3
+ # A book.
4
+ #
5
+ # A book is offered on many venues and has many offers through those venues.
6
+ class Book < Structure
7
+ key :isbn
8
+ key :asin
9
+ key :offers, :type => Array, :default => []
10
+
11
+ # Returns the best kosher offer or nil if there are none.
4
12
  def best_kosher_offer
5
13
  offer = offers.sort.first
6
-
7
- (offer && offer.kosher?) ? offer : nil
14
+ offer && offer.kosher? ? offer : nil
8
15
  end
9
16
  end
10
17
  end
@@ -1,9 +1,18 @@
1
1
  module Kosher
2
- class Condition < Struct.new(:grade)
2
+
3
+ # The condition of a book.
4
+ #
5
+ # This condition is modeled on a numeric range starting from 1, which
6
+ # represents `new' offers.
7
+ class Condition < Structure
3
8
  include Threshold
4
9
 
5
- self.threshold = 4
10
+ key :grade, :type => Integer
6
11
 
12
+ # Returns whether the condition is kosher.
13
+ #
14
+ # A condition is kosher if its grade is below a minimum threshold. We
15
+ # usually consider books that are good or better kosher.
7
16
  def kosher?
8
17
  grade <= threshold
9
18
  end
@@ -1,5 +1,8 @@
1
1
  module Kosher
2
- class Description < Struct.new(:text)
2
+
3
+ # The seller's description of the offer.
4
+ class Description < Structure
5
+ key :text, :default => ''
3
6
 
4
7
  DAMAGED = "\\b(?:missing|torn|broken|split|discard|withdrawn|rent|stain|school|damaged|water)"
5
8
  EXLIB = "(?:e?x|discarded|retired|former|has|have)[\\s._-]*lib"
@@ -8,7 +11,7 @@ module Kosher
8
11
  REVIEW_COPY = "\\b(?:uncorrected|advanced?\\sreview|arc)\\b"
9
12
 
10
13
  def damaged?
11
- matches?(DAMAGED)
14
+ matches? DAMAGED
12
15
  end
13
16
 
14
17
  def ex_lib?
@@ -16,7 +19,7 @@ module Kosher
16
19
  end
17
20
 
18
21
  def kosher?
19
- !(damaged? || ex_lib? || marked? || missing_volume? || review_copy?)
22
+ !damaged? && !ex_lib? && !marked? && !missing_volume? && !review_copy?
20
23
  end
21
24
 
22
25
  def marked?
@@ -24,11 +27,11 @@ module Kosher
24
27
  end
25
28
 
26
29
  def missing_volume?
27
- matches?(MISSING_VOL)
30
+ matches? MISSING_VOL
28
31
  end
29
32
 
30
33
  def review_copy?
31
- matches?(REVIEW_COPY)
34
+ matches? REVIEW_COPY
32
35
  end
33
36
 
34
37
  private
data/lib/kosher/item.rb CHANGED
@@ -1,10 +1,21 @@
1
1
  module Kosher
2
- class Item < Struct.new(:cents, :currency, :quantity, :condition, :description)
2
+
3
+ # The actual item offered by a seller.
4
+ #
5
+ # An item has a price, quantity, condition, and description.
6
+ class Item < Structure
7
+ key :cents, :type => Integer
8
+ key :currency
9
+ key :quantity, :type => Integer
10
+ key :condition, :type => Structure
11
+ key :description, :type => Structure
12
+
3
13
  def kosher?
4
14
  condition.kosher? && description.kosher?
5
15
  end
6
16
 
7
17
  def price
18
+ raise TypeError, "Can't render money" unless cents
8
19
  Money.new(cents, currency)
9
20
  end
10
21
  end
@@ -1,4 +1,8 @@
1
1
  module Kosher
2
- class Location < Struct.new(:country, :state)
2
+
3
+ # The location of a seller.
4
+ class Location < Structure
5
+ key :country
6
+ key :state
3
7
  end
4
8
  end
data/lib/kosher/offer.rb CHANGED
@@ -1,17 +1,16 @@
1
1
  module Kosher
2
2
 
3
- # An offer by a book seller.
4
- class Offer < Struct.new(:id, :venue, :item, :seller, :shipping, :readable)
5
-
3
+ # An offer.
4
+ #
5
+ # This is an actual item offered on a particular venue by a seller.
6
+ class Offer < Structure
6
7
  include Comparable
7
8
 
8
- class << self
9
-
10
- # The base currency.
11
- attr_accessor :base_currency
12
- end
13
-
14
- self.base_currency = 'EUR'
9
+ key :id
10
+ key :venue
11
+ key :item, :type => Structure
12
+ key :seller, :type => Structure
13
+ key :shipping, :type => Structure
15
14
 
16
15
  # Compares offer with another offer.
17
16
  #
@@ -21,29 +20,20 @@ module Kosher
21
20
  if kosher? != other.kosher?
22
21
  kosher? ? -1 : 1
23
22
  else
24
- exchanged_price <=> other.exchanged_price
23
+ price <=> other.price
25
24
  end
26
25
  end
27
26
 
28
- # The price exchanged to the base currency.
29
- def exchanged_price
30
- price.exchange_to(base_currency)
31
- end
32
-
33
27
  # Returns whether the offer is kosher.
28
+ #
29
+ # An offer is kosher if its item, seller, and shipping are kosher.
34
30
  def kosher?
35
31
  item.kosher? && seller.kosher? && shipping.kosher?
36
32
  end
37
33
 
38
- # The total price, including the shipping cost.
34
+ # The total price of an offer.
39
35
  def price
40
36
  item.price + shipping.cost
41
37
  end
42
-
43
- private
44
-
45
- def base_currency
46
- self.class.base_currency
47
- end
48
38
  end
49
39
  end
data/lib/kosher/seller.rb CHANGED
@@ -1,22 +1,25 @@
1
1
  module Kosher
2
- class Seller < Struct.new(:id, :name, :rating, :location)
3
- include Threshold
4
-
5
- class << self
6
- attr_accessor :blacklist
7
- end
8
2
 
9
- self.threshold = 4.8
10
- self.blacklist = []
3
+ # The seller offering a book on a venue.
4
+ #
5
+ # A seller may have a location.
6
+ class Seller < Structure
7
+ include Threshold
11
8
 
12
- def blacklist
13
- self.class.blacklist
14
- end
9
+ key :id
10
+ key :name
11
+ key :rating, :type => Float
12
+ key :location, :type => Structure
15
13
 
14
+ # Returns whether we blacklist the seller.
16
15
  def blacklisted?
17
- blacklist.include?(id)
16
+ Kosher.seller_blacklist.include? id
18
17
  end
19
18
 
19
+ # Returns whether the seller is kosher.
20
+ #
21
+ # A seller is kosher as long as he is not blacklisted and his rating is
22
+ # unknown, 0.0, or above our minimum threshold.
20
23
  def kosher?
21
24
  !blacklisted? && (rating.to_f == 0.0 || rating >= threshold)
22
25
  end
@@ -1,15 +1,26 @@
1
1
  module Kosher
2
- class Shipping < Struct.new(:cents, :currency, :availability)
2
+ # Shipping details of an offer.
3
+ #
4
+ # Shipping costs something (or nothing) and is subject to availability.
5
+ class Shipping < Structure
6
+ key :cents, :type => Integer
7
+ key :currency
8
+ key :availability, :type => Structure
9
+
10
+ # Returns whether the item ships for free.
3
11
  def free?
4
- cents.to_i == 0
12
+ cents == 0
5
13
  end
6
14
 
15
+ # Returns true if the item is available to ship.
7
16
  def kosher?
8
17
  availability.kosher?
9
18
  end
10
19
 
20
+ # The shipping cost.
11
21
  def cost
12
- Money.new(cents.to_i, currency)
22
+ raise TypeError, "Can't render money" unless cents
23
+ Money.new(cents, currency)
13
24
  end
14
25
  end
15
26
  end
@@ -9,13 +9,7 @@ module Kosher
9
9
  end
10
10
 
11
11
  module ClassMethods
12
- def threshold
13
- @threshold
14
- end
15
-
16
- def threshold=(value)
17
- @threshold = value
18
- end
12
+ attr_accessor :threshold
19
13
  end
20
14
  end
21
15
  end
@@ -1,3 +1,3 @@
1
1
  module Kosher
2
- VERSION = '0.2.24'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/kosher.rb CHANGED
@@ -1,7 +1,14 @@
1
+ # Kosher is a somewhat overengineered attempt to abstract bookdealing into a
2
+ # set of models.
3
+
1
4
  require 'money'
5
+ require 'structure'
6
+ require 'structure/json'
2
7
 
8
+ # Require this module before all models.
3
9
  require 'kosher/threshold'
4
10
 
11
+ # Require the models.
5
12
  require 'kosher/availability'
6
13
  require 'kosher/book'
7
14
  require 'kosher/condition'
@@ -9,6 +16,20 @@ require 'kosher/description'
9
16
  require 'kosher/item'
10
17
  require 'kosher/location'
11
18
  require 'kosher/offer'
12
- require 'kosher/readable'
13
19
  require 'kosher/seller'
14
20
  require 'kosher/shipping'
21
+
22
+ module Kosher
23
+ class << self
24
+ attr_writer :seller_blacklist
25
+
26
+ def seller_blacklist
27
+ @seller_blacklist ||= []
28
+ end
29
+ end
30
+
31
+ # Set threshold defaults.
32
+ Availability.threshold = 48
33
+ Condition.threshold = 4
34
+ Seller.threshold = 4.8
35
+ end
@@ -12,15 +12,27 @@ module Kosher
12
12
  end
13
13
 
14
14
  context "when available within threshold" do
15
- it "returns true" do
15
+ before do
16
16
  @availability.hours = 48
17
+ end
18
+
19
+ it "returns true" do
17
20
  @availability.should be_kosher
18
21
  end
19
22
  end
20
23
 
21
24
  context "when not available within threshold" do
22
- it "returns false" do
25
+ before do
23
26
  @availability.hours = 96
27
+ end
28
+
29
+ it "should return false" do
30
+ @availability.should_not be_kosher
31
+ end
32
+ end
33
+
34
+ context "when availability is not known" do
35
+ it "should return false" do
24
36
  @availability.should_not be_kosher
25
37
  end
26
38
  end
@@ -5,7 +5,6 @@ module Kosher
5
5
  describe "#best_kosher_offer" do
6
6
  before do
7
7
  @book = Book.new
8
- @book.offers = []
9
8
 
10
9
  @offer1 = Offer.new
11
10
  @offer2 = Offer.new
@@ -4,17 +4,14 @@ module Kosher
4
4
  describe Item do
5
5
  before do
6
6
  @item = Item.new
7
+ @item.condition = Condition.new(:grade => 1)
7
8
  end
8
9
 
9
10
  describe "#kosher?" do
10
11
  context "when condition is kosher" do
11
- before do
12
- @item.condition = Condition.new(1)
13
- end
14
-
15
12
  context "when description is kosher" do
16
13
  before do
17
- @item.description = Description.new('')
14
+ @item.description = Description.new
18
15
  end
19
16
 
20
17
  it "returns true" do
@@ -24,7 +21,8 @@ module Kosher
24
21
 
25
22
  context "when description is not kosher" do
26
23
  before do
27
- @item.description = Description.new('Withdrawn library book')
24
+ @item.description = Description.new(
25
+ :text => 'Withdrawn library book')
28
26
  end
29
27
 
30
28
  it "returns false" do
@@ -35,7 +33,7 @@ module Kosher
35
33
 
36
34
  context "when condition is not kosher" do
37
35
  before do
38
- @item.condition = Condition.new(5)
36
+ @item.condition.grade = 5
39
37
  end
40
38
 
41
39
  it "returns false" do
@@ -43,5 +41,15 @@ module Kosher
43
41
  end
44
42
  end
45
43
  end
44
+
45
+ describe "#price" do
46
+ context "when no cents are specified" do
47
+ it "raises an error" do
48
+ expect do
49
+ @item.price
50
+ end.to raise_error TypeError
51
+ end
52
+ end
53
+ end
46
54
  end
47
55
  end
@@ -9,12 +9,6 @@ module Kosher
9
9
  @offer.shipping = Shipping.new
10
10
  end
11
11
 
12
- describe "#base_currency" do
13
- it "defaults to EUR" do
14
- @offer.send(:base_currency).should eql 'EUR'
15
- end
16
- end
17
-
18
12
  describe "#kosher?" do
19
13
  context "when item is kosher" do
20
14
  before do
@@ -6,15 +6,9 @@ module Kosher
6
6
  @seller = Seller.new
7
7
  end
8
8
 
9
- describe "#blacklist" do
10
- it "defaults to an empty array" do
11
- @seller.blacklist.should eql []
12
- end
13
- end
14
-
15
9
  describe "#blacklisted?" do
16
10
  before do
17
- Seller.blacklist = ['foo']
11
+ Kosher.seller_blacklist = ['foo']
18
12
  end
19
13
 
20
14
  it "returns true if the seller is blacklisted" do
@@ -54,7 +48,7 @@ module Kosher
54
48
 
55
49
  it "returns false if seller is blacklisted" do
56
50
  @seller.id = ['foo']
57
- Seller.blacklist = [@seller.id]
51
+ Kosher.seller_blacklist = [@seller.id]
58
52
  @seller.should_not be_kosher
59
53
  end
60
54
  end
@@ -14,13 +14,6 @@ module Kosher
14
14
  end
15
15
  end
16
16
 
17
- context "when shipping costs nil" do
18
- it "returns true" do
19
- @shipping.cents = nil
20
- @shipping.should be_free
21
- end
22
- end
23
-
24
17
  context "when shipping is not free" do
25
18
  it "returns false" do
26
19
  @shipping.cents = 1
@@ -32,23 +25,26 @@ module Kosher
32
25
  describe "#kosher?" do
33
26
  context "when available" do
34
27
  it "returns true" do
35
- @shipping.availability = Availability.new(0)
28
+ @shipping.availability = Availability.new(:hours => 0)
36
29
  @shipping.should be_kosher
37
30
  end
38
31
  end
39
32
 
40
33
  context "when not available" do
41
34
  it "returns false" do
42
- @shipping.availability = Availability.new(96)
35
+ @shipping.availability = Availability.new(:hours => 96)
43
36
  @shipping.should_not be_kosher
44
37
  end
45
38
  end
46
39
  end
47
40
 
48
41
  describe "#cost" do
49
- it "returns 0 if no cents given" do
50
- @shipping.cents = nil
51
- @shipping.cost.cents.should eql 0
42
+ context "when no cents are specified" do
43
+ it "raises an error" do
44
+ expect do
45
+ @shipping.cost
46
+ end.to raise_error TypeError
47
+ end
52
48
  end
53
49
  end
54
50
  end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kosher do
4
+ end
metadata CHANGED
@@ -1,8 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kosher
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.2.24
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
6
10
  platform: ruby
7
11
  authors:
8
12
  - Paper Cavalier
@@ -10,20 +14,69 @@ autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
16
 
13
- date: 2011-05-11 00:00:00 +01:00
17
+ date: 2011-05-27 00:00:00 +01:00
14
18
  default_executable:
15
19
  dependencies:
16
20
  - !ruby/object:Gem::Dependency
17
- name: money
21
+ name: rspec
18
22
  prerelease: false
19
23
  requirement: &id001 !ruby/object:Gem::Requirement
20
24
  none: false
21
25
  requirements:
22
26
  - - ~>
23
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 6
31
+ - 0
32
+ version: 2.6.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: ruby-debug19
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 11
46
+ - 6
47
+ version: 0.11.6
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: money
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 3
60
+ - 6
61
+ - 1
24
62
  version: 3.6.1
25
63
  type: :runtime
26
- version_requirements: *id001
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: structure
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ - 5
76
+ - 0
77
+ version: 0.5.0
78
+ type: :runtime
79
+ version_requirements: *id004
27
80
  description: Kosher is a somewhat overengineered attempt to abstract bookselling into a set of models.
28
81
  email: code@papercavalier.com
29
82
  executables: []
@@ -47,7 +100,6 @@ files:
47
100
  - lib/kosher/item.rb
48
101
  - lib/kosher/location.rb
49
102
  - lib/kosher/offer.rb
50
- - lib/kosher/readable.rb
51
103
  - lib/kosher/seller.rb
52
104
  - lib/kosher/shipping.rb
53
105
  - lib/kosher/threshold.rb
@@ -59,9 +111,9 @@ files:
59
111
  - spec/kosher/item_spec.rb
60
112
  - spec/kosher/location_spec.rb
61
113
  - spec/kosher/offer_spec.rb
62
- - spec/kosher/readable_spec.rb
63
114
  - spec/kosher/seller_spec.rb
64
115
  - spec/kosher/shipping_spec.rb
116
+ - spec/kosher_spec.rb
65
117
  - spec/spec_helper.rb
66
118
  has_rdoc: true
67
119
  homepage: https://rubygems.org/gems/kosher
@@ -77,17 +129,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
129
  requirements:
78
130
  - - ">="
79
131
  - !ruby/object:Gem::Version
132
+ segments:
133
+ - 0
80
134
  version: "0"
81
135
  required_rubygems_version: !ruby/object:Gem::Requirement
82
136
  none: false
83
137
  requirements:
84
138
  - - ">="
85
139
  - !ruby/object:Gem::Version
140
+ segments:
141
+ - 0
86
142
  version: "0"
87
143
  requirements: []
88
144
 
89
145
  rubyforge_project: kosher
90
- rubygems_version: 1.6.2
146
+ rubygems_version: 1.3.7
91
147
  signing_key:
92
148
  specification_version: 3
93
149
  summary: A somewhat overengineered attempt to abstract bookselling into a set of models
@@ -99,7 +155,7 @@ test_files:
99
155
  - spec/kosher/item_spec.rb
100
156
  - spec/kosher/location_spec.rb
101
157
  - spec/kosher/offer_spec.rb
102
- - spec/kosher/readable_spec.rb
103
158
  - spec/kosher/seller_spec.rb
104
159
  - spec/kosher/shipping_spec.rb
160
+ - spec/kosher_spec.rb
105
161
  - spec/spec_helper.rb
@@ -1,4 +0,0 @@
1
- module Kosher
2
- class Readable < Struct.new(:price, :description)
3
- end
4
- end
@@ -1,5 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Kosher
4
- describe Readable
5
- end