kosher 0.2.24 → 0.3.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/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