project_euler_cli 1.2.0 → 1.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/project_euler_cli.rb +1 -2
- data/lib/project_euler_cli/archive_searcher.rb +8 -10
- data/lib/project_euler_cli/archive_viewer.rb +12 -12
- data/lib/project_euler_cli/cli.rb +12 -12
- data/lib/project_euler_cli/concerns/scraper.rb +12 -14
- data/lib/project_euler_cli/problem.rb +21 -4
- data/lib/project_euler_cli/version.rb +1 -1
- metadata +2 -3
- data/lib/project_euler_cli/archive_controller.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c66d200a7ebd8baa0a733e1ad106595280b60a1dace740083df802b6b2c6cfb0
|
4
|
+
data.tar.gz: 19437da2e5cf758d9a7f5476672e6889e8e25f46d3e8fab0902de80d70f39497
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c2fd76b1c7fee6762f642d980358a95a45190e39996886046bfd191b2002c074f2f633164f85d3b789d1afa8184957ef856c650452ad00f0740b23a75918d93
|
7
|
+
data.tar.gz: ce9fe335d2d88c0def96b18b85e0ffd8f9a4735f8ce85125eab05eff9643114a1811c1dbc1423c55a4879906ae9a86da2c14185c595a6f045b0f72a894a94ebf
|
data/Gemfile.lock
CHANGED
data/lib/project_euler_cli.rb
CHANGED
@@ -4,10 +4,9 @@ require 'nokogiri'
|
|
4
4
|
require 'pry'
|
5
5
|
|
6
6
|
require "project_euler_cli/version"
|
7
|
-
require "project_euler_cli/problem"
|
8
7
|
require "project_euler_cli/page"
|
8
|
+
require "project_euler_cli/problem"
|
9
9
|
require "project_euler_cli/concerns/scraper"
|
10
|
-
require "project_euler_cli/archive_controller"
|
11
10
|
require "project_euler_cli/archive_viewer"
|
12
11
|
require "project_euler_cli/archive_searcher"
|
13
12
|
require "project_euler_cli/cli"
|
@@ -9,9 +9,7 @@ class ArchiveSearcher
|
|
9
9
|
# Tracks whether there is an active search
|
10
10
|
attr_accessor :searching
|
11
11
|
|
12
|
-
def initialize
|
13
|
-
@problems = problems
|
14
|
-
|
12
|
+
def initialize
|
15
13
|
@results = []
|
16
14
|
@searching = false
|
17
15
|
@initial_search = true
|
@@ -21,27 +19,27 @@ class ArchiveSearcher
|
|
21
19
|
def load_keywords
|
22
20
|
puts "updating keywords..."
|
23
21
|
|
24
|
-
0.upto(Page.total) { |page| load_page(page
|
22
|
+
0.upto(Page.total) { |page| load_page(page) }
|
25
23
|
end
|
26
24
|
|
27
25
|
# Performs a simple search of the problems. It accepts multiple terms and
|
28
26
|
# recognizes quoted phrases. Results will contain *all* of the search terms.
|
29
27
|
#
|
30
28
|
# * +terms+ - String of search terms
|
31
|
-
def search(
|
29
|
+
def search(search_string)
|
32
30
|
load_keywords if Page.visited != (0..Page.total).to_a
|
33
31
|
|
34
32
|
puts "searching..."
|
35
33
|
@searching = true
|
36
34
|
|
37
|
-
|
38
|
-
terms =
|
39
|
-
terms.each { |term|
|
35
|
+
search_string.downcase!
|
36
|
+
terms = search_string.scan(/"[^"]*"/)
|
37
|
+
terms.each { |term| search_string.slice!(term) }
|
40
38
|
terms.collect! { |term| term.gsub("\"", '') }
|
41
|
-
terms +=
|
39
|
+
terms += search_string.split(' ')
|
42
40
|
|
43
41
|
@results = (1..Problem.total).select do |i|
|
44
|
-
terms.all? { |term|
|
42
|
+
terms.all? { |term| Problem[i].title.downcase.include?(term) }
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
@@ -4,31 +4,31 @@ module ProjectEulerCli
|
|
4
4
|
class ArchiveViewer
|
5
5
|
include Scraper
|
6
6
|
|
7
|
-
def initialize
|
8
|
-
|
7
|
+
def initialize
|
8
|
+
lookup_totals
|
9
9
|
end
|
10
10
|
|
11
11
|
# Displays the 10 most recently added problems.
|
12
12
|
def display_recent
|
13
|
-
load_recent
|
13
|
+
load_recent
|
14
14
|
|
15
15
|
puts
|
16
16
|
|
17
17
|
(Problem.total).downto(Problem.total - 9) do |i|
|
18
|
-
puts "#{i} - #{
|
18
|
+
puts "#{i} - #{Problem[i].title}"
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
# Displays the problem numbers and titles for an individual page of the
|
23
23
|
# archive.
|
24
24
|
def display_page(page)
|
25
|
-
load_page(page
|
25
|
+
load_page(page)
|
26
26
|
|
27
27
|
puts
|
28
28
|
|
29
29
|
start = (page - 1) * Page::LENGTH + 1
|
30
30
|
start.upto(start + Page::LENGTH - 1) do |i|
|
31
|
-
puts "#{i} - #{
|
31
|
+
puts "#{i} - #{Problem[i].title}" unless i >= Problem.total - 9
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -36,17 +36,17 @@ class ArchiveViewer
|
|
36
36
|
#
|
37
37
|
# * +id+ - ID of the problem to be displayed
|
38
38
|
def display_problem(id)
|
39
|
-
load_problem_details(id
|
39
|
+
load_problem_details(id)
|
40
40
|
|
41
41
|
puts
|
42
42
|
puts "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
|
43
43
|
puts
|
44
|
-
puts
|
44
|
+
puts Problem[id].title.upcase
|
45
45
|
puts "Problem #{id}"
|
46
46
|
puts
|
47
|
-
puts
|
48
|
-
puts
|
49
|
-
puts
|
47
|
+
puts Problem[id].published
|
48
|
+
puts Problem[id].solved_by
|
49
|
+
puts Problem[id].difficulty if id < Problem.total - 9
|
50
50
|
puts
|
51
51
|
puts "https://projecteuler.net/problem=#{id}"
|
52
52
|
puts
|
@@ -58,7 +58,7 @@ class ArchiveViewer
|
|
58
58
|
# * +list+ - Array of problem IDs
|
59
59
|
def display_custom_page(list)
|
60
60
|
puts
|
61
|
-
list.each { |id| puts "#{id} - #{
|
61
|
+
list.each { |id| puts "#{id} - #{Problem[id].title}" }
|
62
62
|
end
|
63
63
|
|
64
64
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module ProjectEulerCli #:nodoc:
|
2
2
|
|
3
|
-
# Manages the command line interface for the program.
|
4
|
-
# ArchiveController to access the site data.
|
3
|
+
# Manages the command line interface for the program.
|
5
4
|
class CLI
|
6
5
|
|
7
6
|
def initialize
|
8
|
-
@
|
7
|
+
@av = ArchiveViewer.new
|
8
|
+
@as = ArchiveSearcher.new
|
9
9
|
end
|
10
10
|
|
11
11
|
def start
|
@@ -51,7 +51,7 @@ class CLI
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def recent_menu
|
54
|
-
@
|
54
|
+
@av.display_recent
|
55
55
|
|
56
56
|
puts
|
57
57
|
puts "e(x)it"
|
@@ -69,7 +69,7 @@ class CLI
|
|
69
69
|
|
70
70
|
def page_menu(page)
|
71
71
|
page = [1, page, Page.total].sort[1] #clamp
|
72
|
-
@
|
72
|
+
@av.display_page(page)
|
73
73
|
|
74
74
|
puts
|
75
75
|
puts "[#{page}/#{Page.total}] (n)ext (p)rev (g)oto e(x)it"
|
@@ -95,7 +95,7 @@ class CLI
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def problem_menu(id)
|
98
|
-
@
|
98
|
+
@av.display_problem(id)
|
99
99
|
|
100
100
|
puts
|
101
101
|
puts "(b)ack e(x)it"
|
@@ -103,10 +103,10 @@ class CLI
|
|
103
103
|
input = prompt
|
104
104
|
|
105
105
|
if input == 'b'
|
106
|
-
if @
|
106
|
+
if @as.searching
|
107
107
|
search_results_menu
|
108
108
|
else
|
109
|
-
page =
|
109
|
+
page = Problem.page(id)
|
110
110
|
page == 0 ? recent_menu : page_menu(page)
|
111
111
|
end
|
112
112
|
elsif input == 'x'
|
@@ -120,24 +120,24 @@ class CLI
|
|
120
120
|
print "search: "
|
121
121
|
|
122
122
|
search_terms = gets.strip
|
123
|
-
@
|
123
|
+
@as.search(search_terms)
|
124
124
|
search_results_menu
|
125
125
|
end
|
126
126
|
|
127
127
|
def search_results_menu
|
128
|
-
@
|
128
|
+
@av.display_custom_page(@as.results)
|
129
129
|
|
130
130
|
puts
|
131
131
|
puts "(s)earch e(x)it"
|
132
132
|
|
133
133
|
input = prompt
|
134
134
|
|
135
|
-
if @
|
135
|
+
if @as.results.include?(input.to_i)
|
136
136
|
problem_menu(input.to_i)
|
137
137
|
elsif input == 's'
|
138
138
|
search_menu
|
139
139
|
elsif input == 'x'
|
140
|
-
@
|
140
|
+
@as.searching = false
|
141
141
|
return
|
142
142
|
else
|
143
143
|
search_results_menu
|
@@ -15,12 +15,10 @@ module Scraper
|
|
15
15
|
|
16
16
|
# The newest problem is the first one listed on the recent page. The ID
|
17
17
|
# of this problem will always equal the total number of problems.
|
18
|
-
|
18
|
+
id_col.first.text.to_i.times { Problem.new }
|
19
19
|
# There are ten problems on the recent page, so the last archive problem
|
20
|
-
# can be found by subtracting 10 from the total number of problems.
|
21
|
-
|
22
|
-
last_archive_id = Problem.total - 10
|
23
|
-
Page.total = (last_archive_id - 1) / Page::LENGTH + 1
|
20
|
+
# can be found by subtracting 10 from the total number of problems.
|
21
|
+
Page.total = Problem.page(Problem.total - 10)
|
24
22
|
end
|
25
23
|
rescue Timeout::Error
|
26
24
|
puts "Project Euler is not responding."
|
@@ -30,7 +28,7 @@ module Scraper
|
|
30
28
|
end
|
31
29
|
|
32
30
|
# Loads in all of the problem numbers and titles from the recent page.
|
33
|
-
def load_recent
|
31
|
+
def load_recent
|
34
32
|
return if Page.visited.include?(0)
|
35
33
|
|
36
34
|
html = open("https://projecteuler.net/recent")
|
@@ -40,7 +38,7 @@ module Scraper
|
|
40
38
|
|
41
39
|
i = Problem.total
|
42
40
|
problem_links.each do |link|
|
43
|
-
|
41
|
+
Problem[i].title = link.text
|
44
42
|
i -= 1
|
45
43
|
end
|
46
44
|
|
@@ -48,7 +46,7 @@ module Scraper
|
|
48
46
|
end
|
49
47
|
|
50
48
|
# Loads the problem numbers and titles for an individual page of the archive.
|
51
|
-
def load_page(page
|
49
|
+
def load_page(page)
|
52
50
|
return if Page.visited.include?(page)
|
53
51
|
|
54
52
|
html = open("https://projecteuler.net/archives;page=#{page}")
|
@@ -58,7 +56,7 @@ module Scraper
|
|
58
56
|
|
59
57
|
i = (page - 1) * Page::LENGTH + 1
|
60
58
|
problem_links.each do |link|
|
61
|
-
|
59
|
+
Problem[i].title = link.text
|
62
60
|
i += 1
|
63
61
|
end
|
64
62
|
|
@@ -66,8 +64,8 @@ module Scraper
|
|
66
64
|
end
|
67
65
|
|
68
66
|
# Loads the details of an individual problem.
|
69
|
-
def load_problem_details(id
|
70
|
-
return unless
|
67
|
+
def load_problem_details(id)
|
68
|
+
return unless Problem[id].published.nil?
|
71
69
|
|
72
70
|
html = open("https://projecteuler.net/problem=#{id}")
|
73
71
|
fragment = Nokogiri::HTML(html)
|
@@ -75,11 +73,11 @@ module Scraper
|
|
75
73
|
problem_info = fragment.css('div#problem_info span span')
|
76
74
|
|
77
75
|
details = problem_info.text.split(';')
|
78
|
-
|
79
|
-
|
76
|
+
Problem[id].published = details[0].strip
|
77
|
+
Problem[id].solved_by = details[1].strip
|
80
78
|
|
81
79
|
# recent problems do not have a difficult rating
|
82
|
-
|
80
|
+
Problem[id].difficulty = details[2].strip if id <= Problem.total - 10
|
83
81
|
end
|
84
82
|
|
85
83
|
end
|
@@ -3,14 +3,31 @@ module ProjectEulerCli
|
|
3
3
|
class Problem
|
4
4
|
attr_accessor :title, :published, :solved_by, :difficulty
|
5
5
|
|
6
|
-
@@
|
6
|
+
@@all = []
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@@all << self
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[](id)
|
13
|
+
@@all[id - 1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.[]=(id, value)
|
17
|
+
@@all[id - 1] = value
|
18
|
+
end
|
7
19
|
|
8
20
|
def self.total
|
9
|
-
@@
|
21
|
+
@@all.size
|
10
22
|
end
|
11
23
|
|
12
|
-
|
13
|
-
|
24
|
+
# call-seq:
|
25
|
+
# get_page(id) => page
|
26
|
+
#
|
27
|
+
# Returns page number based on the ID of the problem. The recent page is
|
28
|
+
# considered page 0.
|
29
|
+
def self.page(id)
|
30
|
+
id.between?(1, total - 10) ? (id - 1) / Page::LENGTH + 1 : 0
|
14
31
|
end
|
15
32
|
|
16
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: project_euler_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ecssiah
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -102,7 +102,6 @@ files:
|
|
102
102
|
- bin/project-euler-cli
|
103
103
|
- bin/setup
|
104
104
|
- lib/project_euler_cli.rb
|
105
|
-
- lib/project_euler_cli/archive_controller.rb
|
106
105
|
- lib/project_euler_cli/archive_searcher.rb
|
107
106
|
- lib/project_euler_cli/archive_viewer.rb
|
108
107
|
- lib/project_euler_cli/cli.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module ProjectEulerCli
|
2
|
-
|
3
|
-
# Controller class that manages the archive system. It holds the archive data
|
4
|
-
# used by ArchiveViewer and ArchiveSearcher.
|
5
|
-
class ArchiveController
|
6
|
-
include Scraper
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
lookup_totals
|
10
|
-
|
11
|
-
@problems = Array.new(Problem.total + 1) { Problem.new }
|
12
|
-
|
13
|
-
@av = ArchiveViewer.new(@problems)
|
14
|
-
@as = ArchiveSearcher.new(@problems)
|
15
|
-
end
|
16
|
-
|
17
|
-
def searching=(searching)
|
18
|
-
@as.searching = searching
|
19
|
-
end
|
20
|
-
|
21
|
-
def searching
|
22
|
-
@as.searching
|
23
|
-
end
|
24
|
-
|
25
|
-
def search(terms)
|
26
|
-
@as.search(terms)
|
27
|
-
end
|
28
|
-
|
29
|
-
def results_include?(id)
|
30
|
-
@as.results.include?(id)
|
31
|
-
end
|
32
|
-
|
33
|
-
def display_recent
|
34
|
-
@av.display_recent
|
35
|
-
end
|
36
|
-
|
37
|
-
def display_page(page)
|
38
|
-
@av.display_page(page)
|
39
|
-
end
|
40
|
-
|
41
|
-
def display_results
|
42
|
-
@av.display_custom_page(@as.results)
|
43
|
-
end
|
44
|
-
|
45
|
-
def display_problem(id)
|
46
|
-
@av.display_problem(id)
|
47
|
-
end
|
48
|
-
|
49
|
-
# call-seq:
|
50
|
-
# get_page(id) => page
|
51
|
-
#
|
52
|
-
# Returns page number based on the ID of the problem. The recent page is
|
53
|
-
# considered page 0.
|
54
|
-
def get_page(id)
|
55
|
-
id.between?(1, Problem.total - 10) ? (id - 1) / Page::LENGTH + 1 : 0
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|