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.
@@ -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
@@ -0,0 +1,11 @@
1
+ module PlayWhe::Util
2
+ module_function
3
+
4
+ def normalize_year(year)
5
+ "%02d" % (year.to_i % 100)
6
+ end
7
+
8
+ def normalize_month(month)
9
+ Date::ABBR_MONTHNAMES[month.to_i]
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module PlayWhe
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,25 +1,33 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/playwhe/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Dwayne R. Crooks"]
6
- gem.email = ["me@dwaynecrooks.com"]
7
- gem.description = %q{A ruby gem for retrieving and storing Play Whe results.
8
-
9
- The gem provides a ruby API and script for retrieving and storing Play Whe
10
- results from the National Lotteries Control Board (NLCB) website at
11
- http://www.nlcb.co.tt/.}
12
- gem.summary = %q{A ruby gem for retrieving and storing Play Whe results.}
13
- gem.homepage = "http://rubygems.org/gems/playwhe"
14
-
15
- gem.add_dependency('data_mapper', '~> 1.2.0')
16
- gem.add_dependency('dm-sqlite-adapter', '~> 1.2.0')
17
- gem.add_dependency('trollop', '~> 1.16.2')
18
-
19
- gem.files = `git ls-files`.split($\)
20
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
- gem.name = "playwhe"
23
- gem.require_paths = ["lib"]
24
- gem.version = PlayWhe::VERSION
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