eaternet 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.inch.yml +5 -0
- data/Guardfile +36 -0
- data/README.md +23 -23
- data/Rakefile +1 -1
- data/eaternet.gemspec +13 -11
- data/lib/eaternet.rb +8 -4
- data/lib/eaternet/agencies/nyc.rb +44 -20
- data/lib/eaternet/agencies/snhd.rb +24 -21
- data/lib/eaternet/lives_1_0.rb +240 -0
- data/lib/eaternet/loggable.rb +16 -0
- data/lib/eaternet/prototype.rb +112 -0
- data/lib/eaternet/version.rb +1 -1
- data/test/lives_1_0/business_test.rb +7 -10
- data/test/lives_1_0/feed_info_test.rb +3 -6
- data/test/lives_1_0/inspection_test.rb +10 -13
- data/test/lives_1_0/legend_test.rb +23 -26
- data/test/loggable_test.rb +15 -0
- data/test/nyc_adapter_test.rb +8 -11
- data/test/snhd_adapter_test.rb +39 -7
- data/test/test_helper.rb +3 -0
- metadata +38 -4
- data/lib/eaternet/framework/lives_1_0.rb +0 -232
- data/lib/eaternet/framework/prototype.rb +0 -109
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'eaternet/loggable'
|
4
|
+
|
5
|
+
class LoggableTest < Minitest::Test
|
6
|
+
# Test with include because this is how the module
|
7
|
+
# is intended to be used.
|
8
|
+
include Eaternet::Loggable
|
9
|
+
|
10
|
+
def test_creates_and_sends_log_messages_to_stderr
|
11
|
+
assert_output(nil, /Border Collie/) do
|
12
|
+
logger.info('Border Collie')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/test/nyc_adapter_test.rb
CHANGED
@@ -1,22 +1,20 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
require 'eaternet'
|
4
|
-
require 'eaternet/
|
4
|
+
require 'eaternet/lives_1_0'
|
5
5
|
|
6
6
|
CASSETTE = 'nyc'
|
7
7
|
MORRIS_PARK_CSV = 'test/fixtures/morris-park-bake-shop.csv'
|
8
8
|
|
9
9
|
class NycAdapterTest < Minitest::Test
|
10
|
-
|
11
|
-
|
12
|
-
@@nyc = Nyc.new(csv_path: MORRIS_PARK_CSV)
|
10
|
+
@@nyc = Eaternet::Nyc.new(csv_path: MORRIS_PARK_CSV)
|
13
11
|
|
14
12
|
#
|
15
13
|
# Businesses
|
16
14
|
#
|
17
15
|
|
18
16
|
def test_businesses_returns_an_enumerator_of_business
|
19
|
-
assert enumerable_of? Business, @@nyc.businesses
|
17
|
+
assert enumerable_of? Eaternet::Lives_1_0::Business, @@nyc.businesses
|
20
18
|
end
|
21
19
|
|
22
20
|
def test_businesses_returns_lives_attributes
|
@@ -37,7 +35,7 @@ class NycAdapterTest < Minitest::Test
|
|
37
35
|
#
|
38
36
|
|
39
37
|
def test_inspections_returns_an_enumerable_of_inspection
|
40
|
-
assert enumerable_of? Inspection, @@nyc.inspections
|
38
|
+
assert enumerable_of? Eaternet::Lives_1_0::Inspection, @@nyc.inspections
|
41
39
|
end
|
42
40
|
|
43
41
|
# Use the wiki analysis document as a test.
|
@@ -49,7 +47,7 @@ class NycAdapterTest < Minitest::Test
|
|
49
47
|
09/11/2013
|
50
48
|
08/14/2013
|
51
49
|
01/24/2013
|
52
|
-
12/31/2012).map{ |d| Date.strptime(d, '%m/%d/%Y')}
|
50
|
+
12/31/2012).map { |d| Date.strptime(d, '%m/%d/%Y') }
|
53
51
|
actual_dates = @@nyc.inspections.map(&:date).to_a
|
54
52
|
assert_equal expected_dates, actual_dates
|
55
53
|
end
|
@@ -67,7 +65,7 @@ class NycAdapterTest < Minitest::Test
|
|
67
65
|
#
|
68
66
|
|
69
67
|
def test_violations_returns_an_enumerable_of_violation
|
70
|
-
assert enumerable_of? Violation, @@nyc.violations
|
68
|
+
assert enumerable_of? Eaternet::Lives_1_0::Violation, @@nyc.violations
|
71
69
|
end
|
72
70
|
|
73
71
|
def test_violations_returns_lives_attributes
|
@@ -88,11 +86,10 @@ class NycAdapterTest < Minitest::Test
|
|
88
86
|
#
|
89
87
|
|
90
88
|
def test_implements_feed_info
|
91
|
-
assert_instance_of FeedInfo, @@nyc.feed_info
|
89
|
+
assert_instance_of Eaternet::Lives_1_0::FeedInfo, @@nyc.feed_info
|
92
90
|
end
|
93
91
|
|
94
92
|
def test_has_legends
|
95
|
-
assert_instance_of LegendGroup, @@nyc.legends
|
93
|
+
assert_instance_of Eaternet::Lives_1_0::LegendGroup, @@nyc.legends
|
96
94
|
end
|
97
|
-
|
98
95
|
end
|
data/test/snhd_adapter_test.rb
CHANGED
@@ -6,7 +6,19 @@ require 'eaternet'
|
|
6
6
|
SNHD_CASSETTE = 'snhd'
|
7
7
|
|
8
8
|
class SnhdAdapterTest < Minitest::Test
|
9
|
-
@@snhd = Snhd.new
|
9
|
+
@@snhd = Eaternet::Snhd.new
|
10
|
+
|
11
|
+
#
|
12
|
+
# Namespace and access
|
13
|
+
#
|
14
|
+
|
15
|
+
def test_global_namespace_not_polluted
|
16
|
+
assert_raises(NameError) { Snhd }
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# #businesses
|
21
|
+
#
|
10
22
|
|
11
23
|
def test_businesses_returns_an_enumerator
|
12
24
|
VCR.use_cassette(SNHD_CASSETTE) do
|
@@ -16,7 +28,7 @@ class SnhdAdapterTest < Minitest::Test
|
|
16
28
|
|
17
29
|
def test_businesses_returns_business_data_objects
|
18
30
|
VCR.use_cassette(SNHD_CASSETTE) do
|
19
|
-
assert_kind_of Eaternet::
|
31
|
+
assert_kind_of Eaternet::Prototype::BusinessData, @@snhd.businesses.first
|
20
32
|
end
|
21
33
|
end
|
22
34
|
|
@@ -31,6 +43,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
31
43
|
end
|
32
44
|
end
|
33
45
|
|
46
|
+
#
|
47
|
+
# #inspections
|
48
|
+
#
|
49
|
+
|
34
50
|
def test_inspections_returns_an_enumerable
|
35
51
|
VCR.use_cassette(SNHD_CASSETTE) do
|
36
52
|
assert_kind_of Enumerable, @@snhd.inspections
|
@@ -39,7 +55,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
39
55
|
|
40
56
|
def test_inspections_returns_inspection_data_objects
|
41
57
|
VCR.use_cassette(SNHD_CASSETTE) do
|
42
|
-
assert_kind_of
|
58
|
+
assert_kind_of(
|
59
|
+
Eaternet::Prototype::InspectionData,
|
60
|
+
@@snhd.inspections.first
|
61
|
+
)
|
43
62
|
end
|
44
63
|
end
|
45
64
|
|
@@ -53,6 +72,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
53
72
|
end
|
54
73
|
end
|
55
74
|
|
75
|
+
#
|
76
|
+
# #violations
|
77
|
+
#
|
78
|
+
|
56
79
|
def test_violations_returns_an_enumerable
|
57
80
|
VCR.use_cassette(SNHD_CASSETTE) do
|
58
81
|
assert_kind_of Enumerable, @@snhd.violations
|
@@ -61,7 +84,7 @@ class SnhdAdapterTest < Minitest::Test
|
|
61
84
|
|
62
85
|
def test_violations_returns_violation_data_objects
|
63
86
|
VCR.use_cassette(SNHD_CASSETTE) do
|
64
|
-
assert_kind_of Eaternet::
|
87
|
+
assert_kind_of Eaternet::Prototype::ViolationData, @@snhd.violations.first
|
65
88
|
end
|
66
89
|
end
|
67
90
|
|
@@ -74,6 +97,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
74
97
|
end
|
75
98
|
end
|
76
99
|
|
100
|
+
#
|
101
|
+
# #violation_kinds
|
102
|
+
#
|
103
|
+
|
77
104
|
def test_violation_kinds_returns_an_enumerable
|
78
105
|
VCR.use_cassette(SNHD_CASSETTE) do
|
79
106
|
assert_kind_of Enumerable, @@snhd.violation_kinds
|
@@ -82,7 +109,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
82
109
|
|
83
110
|
def test_violation_kinds_returns_violation_kind_data_objects
|
84
111
|
VCR.use_cassette(SNHD_CASSETTE) do
|
85
|
-
assert_kind_of
|
112
|
+
assert_kind_of(
|
113
|
+
Eaternet::Prototype::ViolationKindData,
|
114
|
+
@@snhd.violation_kinds.first
|
115
|
+
)
|
86
116
|
end
|
87
117
|
end
|
88
118
|
|
@@ -92,8 +122,10 @@ class SnhdAdapterTest < Minitest::Test
|
|
92
122
|
assert_equal '1', v.orig_key
|
93
123
|
assert_equal '1', v.code
|
94
124
|
assert_equal '6', v.demerits
|
95
|
-
assert_equal
|
96
|
-
|
125
|
+
assert_equal(
|
126
|
+
'Food not obtained from approved sources and/or improperly identified.',
|
127
|
+
v.description
|
128
|
+
)
|
97
129
|
end
|
98
130
|
end
|
99
131
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eaternet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robb Shecter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: guard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: guard-minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: minitest
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,8 +173,10 @@ extensions: []
|
|
145
173
|
extra_rdoc_files: []
|
146
174
|
files:
|
147
175
|
- ".gitignore"
|
176
|
+
- ".inch.yml"
|
148
177
|
- ".travis.yml"
|
149
178
|
- Gemfile
|
179
|
+
- Guardfile
|
150
180
|
- LICENSE.txt
|
151
181
|
- README.md
|
152
182
|
- Rakefile
|
@@ -155,8 +185,9 @@ files:
|
|
155
185
|
- lib/eaternet/agencies/nyc.rb
|
156
186
|
- lib/eaternet/agencies/snhd.rb
|
157
187
|
- lib/eaternet/agencies/snhd_config.rb
|
158
|
-
- lib/eaternet/
|
159
|
-
- lib/eaternet/
|
188
|
+
- lib/eaternet/lives_1_0.rb
|
189
|
+
- lib/eaternet/loggable.rb
|
190
|
+
- lib/eaternet/prototype.rb
|
160
191
|
- lib/eaternet/util.rb
|
161
192
|
- lib/eaternet/version.rb
|
162
193
|
- lib/ext/lazy.rb
|
@@ -166,6 +197,7 @@ files:
|
|
166
197
|
- test/lives_1_0/feed_info_test.rb
|
167
198
|
- test/lives_1_0/inspection_test.rb
|
168
199
|
- test/lives_1_0/legend_test.rb
|
200
|
+
- test/loggable_test.rb
|
169
201
|
- test/nyc_adapter_test.rb
|
170
202
|
- test/snhd_adapter_test.rb
|
171
203
|
- test/test_helper.rb
|
@@ -200,6 +232,8 @@ test_files:
|
|
200
232
|
- test/lives_1_0/feed_info_test.rb
|
201
233
|
- test/lives_1_0/inspection_test.rb
|
202
234
|
- test/lives_1_0/legend_test.rb
|
235
|
+
- test/loggable_test.rb
|
203
236
|
- test/nyc_adapter_test.rb
|
204
237
|
- test/snhd_adapter_test.rb
|
205
238
|
- test/test_helper.rb
|
239
|
+
has_rdoc:
|
@@ -1,232 +0,0 @@
|
|
1
|
-
module Eaternet
|
2
|
-
module Framework
|
3
|
-
|
4
|
-
# Framework for creating LIVES 1.0 apps.
|
5
|
-
#
|
6
|
-
# The goal is to make it as easy as possible to create
|
7
|
-
# compliant implementations. And so, the Business, Inspection
|
8
|
-
# and Violation data objects use validations to ensure that they
|
9
|
-
# correctly produce the entities listed in the spec.
|
10
|
-
#
|
11
|
-
# @see http://www.yelp.com/healthscores
|
12
|
-
# @see http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
|
13
|
-
# @see http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
|
14
|
-
module Lives_1_0
|
15
|
-
require 'active_model'
|
16
|
-
|
17
|
-
|
18
|
-
class Adapter
|
19
|
-
# Required.
|
20
|
-
# @return [Enumerable<Business>]
|
21
|
-
def businesses
|
22
|
-
fail 'Override this to return an Enumerable of Business'
|
23
|
-
end
|
24
|
-
|
25
|
-
# Required.
|
26
|
-
# @return [Enumerable<Inspection>]
|
27
|
-
def inspections
|
28
|
-
fail 'Override this to return an Enumerable of Inspection'
|
29
|
-
end
|
30
|
-
|
31
|
-
# Optional.
|
32
|
-
# @return [Enumerable<Violation>]
|
33
|
-
def violations
|
34
|
-
fail 'Optionally override this to return an Enumerable of Violation'
|
35
|
-
end
|
36
|
-
|
37
|
-
# Optional.
|
38
|
-
# @return [FeedInfo]
|
39
|
-
def feed_info
|
40
|
-
fail 'Optionally override this to return a FeedInfo'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
class DataTransferObject
|
46
|
-
include ActiveModel::Validations
|
47
|
-
|
48
|
-
def initialize(&block)
|
49
|
-
block.call(self)
|
50
|
-
check_validations!
|
51
|
-
end
|
52
|
-
|
53
|
-
def check_validations!
|
54
|
-
fail ArgumentError, errors.messages.inspect if invalid?
|
55
|
-
end
|
56
|
-
|
57
|
-
class TypeValidator < ActiveModel::EachValidator
|
58
|
-
def validate_each(record, attribute, value)
|
59
|
-
record.errors.add attribute, (options[:message] || "is not of class #{options[:with]}") unless
|
60
|
-
value.class == options[:with]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
|
-
# @see http://www.yelp.com/healthscores#businesses
|
67
|
-
class Business < DataTransferObject
|
68
|
-
attr_accessor :business_id, :name, :address, :city, :state,
|
69
|
-
:postal_code, :latitude, :longitude, :phone_number
|
70
|
-
|
71
|
-
validates :business_id, :name, :address,
|
72
|
-
type: String,
|
73
|
-
presence: true
|
74
|
-
validates :city, :state, :postal_code, :phone_number,
|
75
|
-
type: String,
|
76
|
-
allow_nil: true
|
77
|
-
validates :latitude,
|
78
|
-
numericality:
|
79
|
-
{
|
80
|
-
greater_than_or_equal_to: -90,
|
81
|
-
less_than_or_equal_to: 90
|
82
|
-
},
|
83
|
-
allow_nil: true
|
84
|
-
validates :longitude,
|
85
|
-
numericality:
|
86
|
-
{
|
87
|
-
greater_than_or_equal_to: -180,
|
88
|
-
less_than_or_equal_to: 180
|
89
|
-
},
|
90
|
-
allow_nil: true
|
91
|
-
|
92
|
-
def ==(other)
|
93
|
-
@business_id == other.business_id
|
94
|
-
end
|
95
|
-
|
96
|
-
def eql?(other)
|
97
|
-
self == other
|
98
|
-
end
|
99
|
-
|
100
|
-
def hash
|
101
|
-
@business_id.hash
|
102
|
-
end
|
103
|
-
|
104
|
-
# @return [String]
|
105
|
-
def to_s
|
106
|
-
"Business #{@business_id}"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
# @see http://www.yelp.com/healthscores#inspections
|
112
|
-
class Inspection < DataTransferObject
|
113
|
-
attr_accessor :business_id, :score, :date, :description, :type
|
114
|
-
|
115
|
-
ZERO_TO_ONE_HUNDRED_AND_BLANK = (0..100).to_a + ['']
|
116
|
-
|
117
|
-
validates :business_id,
|
118
|
-
type: String,
|
119
|
-
presence: true
|
120
|
-
validates :score,
|
121
|
-
inclusion: { in: ZERO_TO_ONE_HUNDRED_AND_BLANK },
|
122
|
-
allow_nil: true
|
123
|
-
validates :date,
|
124
|
-
type: Date,
|
125
|
-
presence: true
|
126
|
-
validates :type,
|
127
|
-
inclusion: { in: %w(initial routine followup complaint) },
|
128
|
-
allow_nil: true
|
129
|
-
|
130
|
-
def score
|
131
|
-
@score.nil? ? '' : @score
|
132
|
-
end
|
133
|
-
|
134
|
-
def to_s
|
135
|
-
"Inspection #{@business_id}/#{@date}/#{@score}"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
# @see http://www.yelp.com/healthscores#violations
|
141
|
-
class Violation < DataTransferObject
|
142
|
-
attr_accessor :business_id, :date, :code, :description
|
143
|
-
|
144
|
-
validates :business_id,
|
145
|
-
type: String,
|
146
|
-
presence: true
|
147
|
-
validates :date,
|
148
|
-
type: Date,
|
149
|
-
presence: true
|
150
|
-
validates :code, :description,
|
151
|
-
type: String,
|
152
|
-
allow_nil: true
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
# @see http://www.yelp.com/healthscores#feed_info
|
157
|
-
class FeedInfo < DataTransferObject
|
158
|
-
attr_accessor :feed_date, :feed_version, :municipality_name,
|
159
|
-
:municipality_url, :contact_email
|
160
|
-
|
161
|
-
HAS_AN_AT_SOMEWHERE_IN_THE_MIDDLE = /\A[^@]+@[^@]+\z/
|
162
|
-
|
163
|
-
validates :feed_date,
|
164
|
-
type: Date,
|
165
|
-
presence: true
|
166
|
-
validates :feed_version,
|
167
|
-
type: String,
|
168
|
-
presence: true
|
169
|
-
validates :municipality_name,
|
170
|
-
type: String,
|
171
|
-
presence: true
|
172
|
-
validates :municipality_url,
|
173
|
-
type: String,
|
174
|
-
format: { with: %r(\Ahttps?:/) },
|
175
|
-
allow_nil: true
|
176
|
-
validates :contact_email,
|
177
|
-
type: String,
|
178
|
-
format: { with: HAS_AN_AT_SOMEWHERE_IN_THE_MIDDLE },
|
179
|
-
allow_nil: true
|
180
|
-
end
|
181
|
-
|
182
|
-
|
183
|
-
# @see http://www.yelp.com/healthscores#legend
|
184
|
-
class Legend < DataTransferObject
|
185
|
-
attr_accessor :minimum_score, :maximum_score, :description
|
186
|
-
|
187
|
-
validates :minimum_score, :maximum_score,
|
188
|
-
inclusion: { in: (0..100) }
|
189
|
-
validates :description,
|
190
|
-
presence: true,
|
191
|
-
type: String
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
# A container for all the Legends in the data set. Performs
|
196
|
-
# validation on the whole set of Legends ensure they cover
|
197
|
-
# the entire range of scores and do not overlap.
|
198
|
-
class LegendGroup < DataTransferObject
|
199
|
-
attr_accessor :legends
|
200
|
-
|
201
|
-
# Check that all the items in this LegendGroup:
|
202
|
-
#
|
203
|
-
# 1. Are of the class, Legend
|
204
|
-
# 2. Cover the range of scores from 0-100 without overlap
|
205
|
-
class ComprehensiveValidator < ActiveModel::EachValidator
|
206
|
-
def validate_each(record, attribute, legends)
|
207
|
-
scores = (0..100).to_a
|
208
|
-
legends.each do |legend|
|
209
|
-
unless legend.class == Legend
|
210
|
-
record.errors.add attribute, 'must be a Legend'
|
211
|
-
return
|
212
|
-
end
|
213
|
-
range = (legend.minimum_score..legend.maximum_score)
|
214
|
-
range.each do |score|
|
215
|
-
if scores.delete(score).nil?
|
216
|
-
unless score == legend.minimum_score || score == legend.maximum_score
|
217
|
-
record.errors.add attribute, 'may not overlap'
|
218
|
-
return
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
record.errors.add attribute, 'do not cover entire span from 0–100' unless scores.empty?
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
validates :legends, comprehensive: true
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|