playwhe 0.1.0 → 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -16
- data/Gemfile +1 -2
- data/LICENSE +17 -18
- data/README.md +98 -89
- data/Rakefile +9 -2
- data/bin/playwhe +99 -74
- data/lib/playwhe.rb +76 -133
- data/lib/playwhe/fetcher.rb +25 -0
- data/lib/playwhe/get.rb +214 -0
- data/lib/playwhe/http.rb +39 -0
- data/lib/playwhe/parser.rb +15 -0
- data/lib/playwhe/result.rb +119 -0
- data/lib/playwhe/settings.rb +15 -0
- data/lib/playwhe/util.rb +11 -0
- data/lib/playwhe/version.rb +1 -1
- data/playwhe.gemspec +32 -24
- data/test/playwhe/fetcher_test.rb +72 -0
- data/test/playwhe/get_test.rb +187 -0
- data/test/playwhe/http/adapter_test.rb +46 -0
- data/test/playwhe/http/response_test.rb +39 -0
- data/test/playwhe/parser_test.rb +34 -0
- data/test/playwhe/result_test.rb +158 -0
- data/test/playwhe/settings_test.rb +13 -0
- data/test/playwhe/util_test.rb +37 -0
- data/test/test_helper.rb +6 -0
- metadata +97 -45
- data/data/playwhe.db +0 -0
- data/lib/playwhe/storage.rb +0 -109
- data/lib/playwhe/storage/models.rb +0 -92
data/lib/playwhe/http.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "http"
|
2
|
+
|
3
|
+
module PlayWhe
|
4
|
+
module HTTP
|
5
|
+
class Response
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
def status
|
13
|
+
response.status
|
14
|
+
end
|
15
|
+
|
16
|
+
def body
|
17
|
+
response.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def ok?
|
21
|
+
status == 200
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Adapter
|
26
|
+
attr_reader :http_client
|
27
|
+
|
28
|
+
def initialize(http_client)
|
29
|
+
@http_client = http_client
|
30
|
+
end
|
31
|
+
|
32
|
+
def post(url, data)
|
33
|
+
Response.new http_client.post(url, form: data)
|
34
|
+
rescue ::HTTP::Error
|
35
|
+
raise PlayWhe::NetworkError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PlayWhe
|
2
|
+
module Parser
|
3
|
+
PATTERN = /Draw #: <\/strong>(?<draw>\d+).*?Date: <\/strong>(?<day>\d{1,2})-(?<month>[A-Z]{3})-(?<year>\d{2}).*?Mark Drawn: <\/strong>(?<mark>\d+).*?Drawn at: <\/strong>(?<period>[A-Z]{2})/i
|
4
|
+
|
5
|
+
def self.parse(html_results)
|
6
|
+
html_results.to_enum(:scan, PATTERN).map do
|
7
|
+
m = $~
|
8
|
+
date = "#{m[:day]}-#{m[:month]}-#{m[:year]}"
|
9
|
+
|
10
|
+
Result.new \
|
11
|
+
draw: m[:draw], date: date, mark: m[:mark], period: m[:period]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
module PlayWhe
|
4
|
+
class Result
|
5
|
+
attr_reader :draw, :date, :mark, :period
|
6
|
+
attr_reader :errors
|
7
|
+
|
8
|
+
def initialize(draw:, date:, mark:, period:)
|
9
|
+
@draw = draw
|
10
|
+
@date = date
|
11
|
+
@mark = mark
|
12
|
+
@period = period
|
13
|
+
clean
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
return true if other.equal?(self)
|
18
|
+
return false unless other.instance_of?(self.class)
|
19
|
+
draw == other.draw &&
|
20
|
+
date == other.date &&
|
21
|
+
mark == other.mark &&
|
22
|
+
period == other.period
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{draw},#{date ? date.strftime('%Y-%m-%d') : '-'},#{mark},#{period}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_valid?(context = nil)
|
30
|
+
validate(context)
|
31
|
+
no_errors?
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :context
|
37
|
+
|
38
|
+
def clean
|
39
|
+
clean_draw
|
40
|
+
clean_date
|
41
|
+
clean_mark
|
42
|
+
clean_period
|
43
|
+
end
|
44
|
+
|
45
|
+
def clean_draw
|
46
|
+
@draw = draw.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def clean_date
|
50
|
+
@date = Date.parse(date.to_s) unless date.is_a?(Date)
|
51
|
+
rescue ArgumentError
|
52
|
+
@date = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def clean_mark
|
56
|
+
@mark = mark.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def clean_period
|
60
|
+
@period = period.to_s.upcase
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate(context)
|
64
|
+
init(context)
|
65
|
+
validate_draw
|
66
|
+
validate_date
|
67
|
+
validate_mark
|
68
|
+
validate_period
|
69
|
+
end
|
70
|
+
|
71
|
+
def init(context)
|
72
|
+
@context = context
|
73
|
+
@errors = {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_error(name, message)
|
77
|
+
@errors[name] ||= []
|
78
|
+
@errors[name] << message
|
79
|
+
end
|
80
|
+
|
81
|
+
def no_errors?
|
82
|
+
errors.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_draw
|
86
|
+
add_error(:draw, "must be a positive integer") unless draw >= 1
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate_date
|
90
|
+
if date.nil?
|
91
|
+
add_error(:date, "must be a date")
|
92
|
+
else
|
93
|
+
if date < BIRTHDAY
|
94
|
+
add_error(:date, "must be greater than Play Whe's birthday")
|
95
|
+
end
|
96
|
+
|
97
|
+
if context && context.year && date.year != context.year
|
98
|
+
add_error(:date, "must be for the correct year (#{context.year})")
|
99
|
+
end
|
100
|
+
|
101
|
+
if context && context.month && date.month != context.month
|
102
|
+
add_error(:date, "must be for the correct month (#{context.month})")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def validate_mark
|
108
|
+
if mark < LOWEST_MARK || mark > HIGHEST_MARK
|
109
|
+
add_error(:mark, "must be between #{LOWEST_MARK} and #{HIGHEST_MARK} inclusive")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def validate_period
|
114
|
+
unless PERIODS.include?(period)
|
115
|
+
add_error(:period, "must be one of #{PERIODS.join(', ')}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "dry-configurable"
|
2
|
+
|
3
|
+
module PlayWhe
|
4
|
+
class Settings
|
5
|
+
extend Dry::Configurable
|
6
|
+
|
7
|
+
setting :http do
|
8
|
+
setting :read_timeout, 5
|
9
|
+
setting :write_timeout, 5
|
10
|
+
setting :connect_timeout, 5
|
11
|
+
end
|
12
|
+
|
13
|
+
setting :url, "http://nlcb.co.tt/app/index.php/pwresults/playwhemonthsum"
|
14
|
+
end
|
15
|
+
end
|
data/lib/playwhe/util.rb
ADDED
data/lib/playwhe/version.rb
CHANGED
data/playwhe.gemspec
CHANGED
@@ -1,25 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
require_relative "lib/playwhe/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.author = "Dwayne Crooks"
|
5
|
+
s.email = "dwayne@playwhesmarter.com"
|
6
|
+
|
7
|
+
s.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ")
|
8
|
+
A Ruby library for retrieving Play Whe results from the
|
9
|
+
National Lotteries Control Board (NLCB) website.
|
10
|
+
DESCRIPTION
|
11
|
+
|
12
|
+
s.summary = "Play Whe results should be easy to get"
|
13
|
+
s.homepage = "https://rubygems.org/gems/playwhe"
|
14
|
+
s.license = "MIT"
|
15
|
+
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
|
20
|
+
s.name = "playwhe"
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.version = PlayWhe::VERSION
|
23
|
+
|
24
|
+
s.required_ruby_version = ">= 2.0"
|
25
|
+
|
26
|
+
s.add_runtime_dependency "http", "~> 2.0", ">= 2.0.2"
|
27
|
+
s.add_runtime_dependency "dry-configurable", "~> 0"
|
28
|
+
|
29
|
+
s.add_development_dependency "bundler", "~> 1.0"
|
30
|
+
s.add_development_dependency "minitest", "~> 5.9"
|
31
|
+
s.add_development_dependency "pry-byebug", "~> 0"
|
32
|
+
s.add_development_dependency "rake", "~> 0"
|
25
33
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
describe PlayWhe::Fetcher do
|
4
|
+
let(:url) { "http://example.com" }
|
5
|
+
let(:year) { 2016 }
|
6
|
+
let(:month) { 1 }
|
7
|
+
|
8
|
+
describe "#get" do
|
9
|
+
it "makes a POST request" do
|
10
|
+
good_response = Object.new
|
11
|
+
def good_response.ok?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
def good_response.body
|
15
|
+
""
|
16
|
+
end
|
17
|
+
|
18
|
+
http_adapter = Minitest::Mock.new
|
19
|
+
http_adapter.expect \
|
20
|
+
:post, good_response, [url, { year: "16", month: "Jan" }]
|
21
|
+
|
22
|
+
fetcher = PlayWhe::Fetcher.new(http_adapter, url)
|
23
|
+
fetcher.get(year: year, month: month)
|
24
|
+
|
25
|
+
expect(http_adapter.verify).must_equal true
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "when the response is good" do
|
29
|
+
it "returns the body of the response" do
|
30
|
+
http_adapter = Object.new
|
31
|
+
def http_adapter.post(*args)
|
32
|
+
good_response = Object.new
|
33
|
+
def good_response.ok?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
def good_response.body
|
37
|
+
"results"
|
38
|
+
end
|
39
|
+
good_response
|
40
|
+
end
|
41
|
+
|
42
|
+
fetcher = PlayWhe::Fetcher.new(http_adapter, url)
|
43
|
+
|
44
|
+
expect(fetcher.get(year: year, month: month)).must_equal "results"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "when the response is bad" do
|
49
|
+
it "raises PlayWhe::BadResponseError" do
|
50
|
+
http_adapter = Object.new
|
51
|
+
def http_adapter.post(*args)
|
52
|
+
bad_response = Object.new
|
53
|
+
def bad_response.ok?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
def bad_response.status
|
57
|
+
400
|
58
|
+
end
|
59
|
+
def bad_response.body
|
60
|
+
""
|
61
|
+
end
|
62
|
+
bad_response
|
63
|
+
end
|
64
|
+
|
65
|
+
fetcher = PlayWhe::Fetcher.new(http_adapter, url)
|
66
|
+
|
67
|
+
expect { fetcher.get(year: year, month: month) }.must_raise \
|
68
|
+
PlayWhe::BadResponseError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class FakeFetcher
|
4
|
+
def initialize(start_year, years: 1, days: 1)
|
5
|
+
@results = []
|
6
|
+
|
7
|
+
draw = 1
|
8
|
+
end_year = start_year + years - 1
|
9
|
+
(start_year..end_year).each do |year|
|
10
|
+
(1..12).each do |month|
|
11
|
+
(1..days).each do |day|
|
12
|
+
date = Date.new(year, month, day)
|
13
|
+
|
14
|
+
PlayWhe::PERIODS.each do |period|
|
15
|
+
@results << make_result(draw, date, period)
|
16
|
+
draw += 1
|
17
|
+
end
|
18
|
+
|
19
|
+
# One bad result per day
|
20
|
+
@results << make_result(draw, date, "XM")
|
21
|
+
draw += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@results.shuffle!
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(year:, month: nil)
|
30
|
+
results.select do |r|
|
31
|
+
if month.nil?
|
32
|
+
r[:date].year == year
|
33
|
+
else
|
34
|
+
r[:date].year == year && r[:date].month == month
|
35
|
+
end
|
36
|
+
end
|
37
|
+
.map { |r| as_html(r) }
|
38
|
+
.join
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :results
|
44
|
+
|
45
|
+
def as_html(result)
|
46
|
+
draw = result[:draw]
|
47
|
+
date = result[:date].strftime("%d-%b-%y")
|
48
|
+
mark = result[:mark]
|
49
|
+
period = result[:period]
|
50
|
+
|
51
|
+
"<strong> Draw #: </strong>#{draw}<br><strong> Date: </strong>#{date}<br><strong> Mark Drawn: </strong>#{mark}<br><strong> Drawn at: </strong>#{period}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def make_result(draw, date, period)
|
55
|
+
mark = (PlayWhe::LOWEST_MARK..PlayWhe::HIGHEST_MARK).to_a.shuffle.first
|
56
|
+
{ draw: draw, date: date, mark: mark, period: period }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe PlayWhe::Get do
|
61
|
+
let(:parser) { PlayWhe::Parser }
|
62
|
+
|
63
|
+
describe "#results_for_year" do
|
64
|
+
it "returns the results for the year of the given date in ascending order" do
|
65
|
+
fetcher = FakeFetcher.new(2014, years: 3) # 2014, 2015, 2016
|
66
|
+
get = PlayWhe::Get.new(fetcher: fetcher, parser: parser)
|
67
|
+
|
68
|
+
good_results, bad_results = get.results_for_year(Date.new(2015))
|
69
|
+
|
70
|
+
expect(good_results.length).must_equal(12 * 1 * 4)
|
71
|
+
|
72
|
+
expect(good_results.first.date).must_equal(Date.new(2015, 1, 1))
|
73
|
+
expect(good_results.first.period).must_equal("EM")
|
74
|
+
|
75
|
+
expect(good_results.last.date).must_equal(Date.new(2015, 12, 1))
|
76
|
+
expect(good_results.last.period).must_equal("PM")
|
77
|
+
|
78
|
+
expect(bad_results.length).must_equal(12 * 1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#results_for_month" do
|
83
|
+
it "returns the results for the year/month of the given date in ascending order" do
|
84
|
+
fetcher = FakeFetcher.new(2015, days: 10)
|
85
|
+
get = PlayWhe::Get.new(fetcher: fetcher, parser: parser)
|
86
|
+
|
87
|
+
good_results, bad_results = get.results_for_month(Date.new(2015, 4))
|
88
|
+
|
89
|
+
expect(good_results.length).must_equal(10 * 4)
|
90
|
+
|
91
|
+
expect(good_results.first.date).must_equal(Date.new(2015, 4, 1))
|
92
|
+
expect(good_results.first.period).must_equal("EM")
|
93
|
+
|
94
|
+
expect(good_results.last.date).must_equal(Date.new(2015, 4, 10))
|
95
|
+
expect(good_results.last.period).must_equal("PM")
|
96
|
+
|
97
|
+
expect(bad_results.length).must_equal(10)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#results_for_day" do
|
102
|
+
it "returns the results for the year/month/day of the given date in ascending order" do
|
103
|
+
fetcher = FakeFetcher.new(2015, days: 10)
|
104
|
+
get = PlayWhe::Get.new(fetcher: fetcher, parser: parser)
|
105
|
+
|
106
|
+
good_results, bad_results = get.results_for_day(Date.new(2015, 10, 10))
|
107
|
+
|
108
|
+
expect(good_results.length).must_equal(4)
|
109
|
+
|
110
|
+
expect(good_results[0].date).must_equal(Date.new(2015, 10, 10))
|
111
|
+
expect(good_results[0].period).must_equal("EM")
|
112
|
+
|
113
|
+
expect(good_results[1].date).must_equal(Date.new(2015, 10, 10))
|
114
|
+
expect(good_results[1].period).must_equal("AM")
|
115
|
+
|
116
|
+
expect(good_results[2].date).must_equal(Date.new(2015, 10, 10))
|
117
|
+
expect(good_results[2].period).must_equal("AN")
|
118
|
+
|
119
|
+
expect(good_results[3].date).must_equal(Date.new(2015, 10, 10))
|
120
|
+
expect(good_results[3].period).must_equal("PM")
|
121
|
+
|
122
|
+
expect(bad_results.length).must_equal(1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#most_recent" do
|
127
|
+
describe "when limit is a positive integer" do
|
128
|
+
it "returns limit results in most recent to least recent order" do
|
129
|
+
fetcher = FakeFetcher.new(2015)
|
130
|
+
get = PlayWhe::Get.new(fetcher: fetcher, parser: parser)
|
131
|
+
|
132
|
+
good_results, bad_results = get.most_recent(limit: 6)
|
133
|
+
|
134
|
+
expect(good_results.length).must_equal(4)
|
135
|
+
|
136
|
+
expect(good_results[0].date).must_equal(Date.new(2015, 12, 1))
|
137
|
+
expect(good_results[0].period).must_equal("PM")
|
138
|
+
|
139
|
+
expect(good_results[1].date).must_equal(Date.new(2015, 12, 1))
|
140
|
+
expect(good_results[1].period).must_equal("AN")
|
141
|
+
|
142
|
+
expect(good_results[2].date).must_equal(Date.new(2015, 12, 1))
|
143
|
+
expect(good_results[2].period).must_equal("AM")
|
144
|
+
|
145
|
+
expect(good_results[3].date).must_equal(Date.new(2015, 12, 1))
|
146
|
+
expect(good_results[3].period).must_equal("EM")
|
147
|
+
|
148
|
+
expect(bad_results.length).must_equal(2)
|
149
|
+
|
150
|
+
expect(bad_results[0].date).must_equal(Date.new(2015, 12, 1))
|
151
|
+
expect(bad_results[1].date).must_equal(Date.new(2015, 11, 1))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#least_recent" do
|
157
|
+
describe "when limit is a positive integer" do
|
158
|
+
it "returns limit results in least recent to most recent order" do
|
159
|
+
fetcher = FakeFetcher.new(2015)
|
160
|
+
get = PlayWhe::Get.new(fetcher: fetcher, parser: parser)
|
161
|
+
|
162
|
+
good_results, bad_results = get.least_recent(limit: 6)
|
163
|
+
|
164
|
+
expect(good_results.length).must_equal(5)
|
165
|
+
|
166
|
+
expect(good_results[0].date).must_equal(Date.new(2015, 1, 1))
|
167
|
+
expect(good_results[0].period).must_equal("EM")
|
168
|
+
|
169
|
+
expect(good_results[1].date).must_equal(Date.new(2015, 1, 1))
|
170
|
+
expect(good_results[1].period).must_equal("AM")
|
171
|
+
|
172
|
+
expect(good_results[2].date).must_equal(Date.new(2015, 1, 1))
|
173
|
+
expect(good_results[2].period).must_equal("AN")
|
174
|
+
|
175
|
+
expect(good_results[3].date).must_equal(Date.new(2015, 1, 1))
|
176
|
+
expect(good_results[3].period).must_equal("PM")
|
177
|
+
|
178
|
+
expect(good_results[4].date).must_equal(Date.new(2015, 2, 1))
|
179
|
+
expect(good_results[4].period).must_equal("EM")
|
180
|
+
|
181
|
+
expect(bad_results.length).must_equal(1)
|
182
|
+
|
183
|
+
expect(bad_results[0].date).must_equal(Date.new(2015, 1, 1))
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|