gnomikologikon-fortune 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 70d4f3c8f0c298fbff7c82d3351a101f90e1158b4735ce8a729b35dbea453a06
4
+ data.tar.gz: 92c96522889a47a0e81fbceff35578273f53f0540706231cb28e3ea5bb608e41
5
+ SHA512:
6
+ metadata.gz: 8ef6cfe7c24f4029bafa58f5a590b07e9c058b3e493c0708c10fac1ea00b5ae1cdbaa500faf0a553e6cc0df4a9838ee3cb869c50ebca097b90907bf62ae84578
7
+ data.tar.gz: 5a85e05a2fe874ae556413e2913be97aa211cd582f16d13a276a1435b6a84b7b96b92af4065a7e78f7bb7decb70ac195c15cbba382f582c6a1d3b009dff22361
data/.gitignore ADDED
@@ -0,0 +1,178 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+
11
+ # Created by https://www.toptal.com/developers/gitignore/api/ruby,rubymine
12
+ # Edit at https://www.toptal.com/developers/gitignore?templates=ruby,rubymine
13
+
14
+ ### Ruby ###
15
+ *.gem
16
+ *.rbc
17
+ /.config
18
+ /coverage/
19
+ /InstalledFiles
20
+ /pkg/
21
+ /spec/reports/
22
+ /spec/examples.txt
23
+ /test/tmp/
24
+ /test/version_tmp/
25
+ /tmp/
26
+
27
+ # Used by dotenv library to load environment variables.
28
+ # .env
29
+
30
+ # Ignore Byebug command history file.
31
+ .byebug_history
32
+
33
+ ## Specific to RubyMotion:
34
+ .dat*
35
+ .repl_history
36
+ build/
37
+ *.bridgesupport
38
+ build-iPhoneOS/
39
+ build-iPhoneSimulator/
40
+
41
+ ## Specific to RubyMotion (use of CocoaPods):
42
+ #
43
+ # We recommend against adding the Pods directory to your .gitignore. However
44
+ # you should judge for yourself, the pros and cons are mentioned at:
45
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
46
+ # vendor/Pods/
47
+
48
+ ## Documentation cache and generated files:
49
+ /.yardoc/
50
+ /_yardoc/
51
+ /doc/
52
+ /rdoc/
53
+
54
+ ## Environment normalization:
55
+ /.bundle/
56
+ /vendor/bundle
57
+ /lib/bundler/man/
58
+
59
+ # for a library or gem, you might want to ignore these files since the code is
60
+ # intended to run in multiple environments; otherwise, check them in:
61
+ # Gemfile.lock
62
+ # .ruby-version
63
+ # .ruby-gemset
64
+
65
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
66
+ .rvmrc
67
+
68
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
69
+ # .rubocop-https?--*
70
+
71
+ ### Ruby Patch ###
72
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
73
+ # .rubocop-https?--*
74
+
75
+ ### RubyMine ###
76
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
77
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
78
+
79
+ # User-specific stuff
80
+ .idea/**/workspace.xml
81
+ .idea/**/tasks.xml
82
+ .idea/**/usage.statistics.xml
83
+ .idea/**/dictionaries
84
+ .idea/**/shelf
85
+
86
+ # Generated files
87
+ .idea/**/contentModel.xml
88
+
89
+ # Sensitive or high-churn files
90
+ .idea/**/dataSources/
91
+ .idea/**/dataSources.ids
92
+ .idea/**/dataSources.local.xml
93
+ .idea/**/sqlDataSources.xml
94
+ .idea/**/dynamic.xml
95
+ .idea/**/uiDesigner.xml
96
+ .idea/**/dbnavigator.xml
97
+
98
+ # Gradle
99
+ .idea/**/gradle.xml
100
+ .idea/**/libraries
101
+
102
+ # Gradle and Maven with auto-import
103
+ # When using Gradle or Maven with auto-import, you should exclude module files,
104
+ # since they will be recreated, and may cause churn. Uncomment if using
105
+ # auto-import.
106
+ # .idea/artifacts
107
+ # .idea/compiler.xml
108
+ # .idea/jarRepositories.xml
109
+ # .idea/modules.xml
110
+ # .idea/*.iml
111
+ # .idea/modules
112
+ # *.iml
113
+ # *.ipr
114
+
115
+ # CMake
116
+ cmake-build-*/
117
+
118
+ # Mongo Explorer plugin
119
+ .idea/**/mongoSettings.xml
120
+
121
+ # File-based project format
122
+ *.iws
123
+
124
+ # IntelliJ
125
+ out/
126
+
127
+ # mpeltonen/sbt-idea plugin
128
+ .idea_modules/
129
+
130
+ # JIRA plugin
131
+ atlassian-ide-plugin.xml
132
+
133
+ # Cursive Clojure plugin
134
+ .idea/replstate.xml
135
+
136
+ # Crashlytics plugin (for Android Studio and IntelliJ)
137
+ com_crashlytics_export_strings.xml
138
+ crashlytics.properties
139
+ crashlytics-build.properties
140
+ fabric.properties
141
+
142
+ # Editor-based Rest Client
143
+ .idea/httpRequests
144
+
145
+ # Android studio 3.1+ serialized cache file
146
+ .idea/caches/build_file_checksums.ser
147
+
148
+ ### RubyMine Patch ###
149
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
150
+
151
+ # *.iml
152
+ # modules.xml
153
+ # .idea/misc.xml
154
+ # *.ipr
155
+
156
+ # Sonarlint plugin
157
+ # https://plugins.jetbrains.com/plugin/7973-sonarlint
158
+ .idea/**/sonarlint/
159
+
160
+ # SonarQube Plugin
161
+ # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
162
+ .idea/**/sonarIssues.xml
163
+
164
+ # Markdown Navigator plugin
165
+ # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
166
+ .idea/**/markdown-navigator.xml
167
+ .idea/**/markdown-navigator-enh.xml
168
+ .idea/**/markdown-navigator/
169
+
170
+ # Cache file creation bug
171
+ # See https://youtrack.jetbrains.com/issue/JBR-2257
172
+ .idea/$CACHE_FILE$
173
+
174
+ # CodeStream plugin
175
+ # https://plugins.jetbrains.com/plugin/12206-codestream
176
+ .idea/codestream.xml
177
+
178
+ # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in gnomikologikon-ruby.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gnomikologikon-fortune (1.0.0)
5
+ httparty
6
+ nokogiri (~> 1.10)
7
+ ruby-progressbar
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ ast (2.4.1)
13
+ httparty (0.18.1)
14
+ mime-types (~> 3.0)
15
+ multi_xml (>= 0.5.2)
16
+ mime-types (3.3.1)
17
+ mime-types-data (~> 3.2015)
18
+ mime-types-data (3.2020.1104)
19
+ minitest (5.14.3)
20
+ multi_xml (0.6.0)
21
+ nokogiri (1.11.1-x86_64-linux)
22
+ racc (~> 1.4)
23
+ parallel (1.20.1)
24
+ parser (3.0.0.0)
25
+ ast (~> 2.4.1)
26
+ racc (1.5.2)
27
+ rainbow (3.0.0)
28
+ rake (13.0.3)
29
+ rdoc (6.3.0)
30
+ regexp_parser (2.0.3)
31
+ rexml (3.2.4)
32
+ rubocop (1.8.1)
33
+ parallel (~> 1.10)
34
+ parser (>= 3.0.0.0)
35
+ rainbow (>= 2.2.2, < 4.0)
36
+ regexp_parser (>= 1.8, < 3.0)
37
+ rexml
38
+ rubocop-ast (>= 1.2.0, < 2.0)
39
+ ruby-progressbar (~> 1.7)
40
+ unicode-display_width (>= 1.4.0, < 3.0)
41
+ rubocop-ast (1.4.0)
42
+ parser (>= 2.7.1.5)
43
+ ruby-progressbar (1.11.0)
44
+ unicode-display_width (2.0.0)
45
+
46
+ PLATFORMS
47
+ x86_64-linux
48
+
49
+ DEPENDENCIES
50
+ gnomikologikon-fortune!
51
+ minitest (~> 5.0)
52
+ rake (~> 13.0)
53
+ rdoc
54
+ rubocop (~> 1.7)
55
+
56
+ BUNDLED WITH
57
+ 2.2.5
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Theodoros Grammenos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ ![CI](https://github.com/teogramm/gnomikologikon-fortune-ruby/workflows/CI/badge.svg)
2
+ # gnomikologikon-fortune
3
+
4
+ Gnomiko is the greek word for "quote".
5
+
6
+ This is an application that downloads quotes from https://gnomikologikon.gr and automatically converts them to files that are ready to use with the fortune command.
7
+
8
+ ## Installation
9
+
10
+ $ gem install gnomikologikon-ruby
11
+
12
+ ## Usage
13
+
14
+ Install and run ``` gnomika ```. The application will ask you about which categories to download.
15
+ Additional options can be viewed with ``` gnomika --help```
16
+
17
+ If you want the cookies to be available with the fortune command you must copy the generated files to the fortune directory (usually /usr/games/fortune or /usr/fortune).
18
+
19
+ ## Development
20
+
21
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
22
+
23
+ ## Contributing
24
+
25
+ Bug reports and pull requests are welcome on GitHub at https://github.com/teogramm/gnomikologikon-fortune.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ require "rdoc/task"
17
+
18
+ RDoc::Task.new do |rdoc|
19
+ rdoc.rdoc_files.include("lib/**/*.rb")
20
+ rdoc.rdoc_dir = "doc"
21
+ end
22
+
23
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/but I think being more free in this regard would be good for the platform as a whole:bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "gnomikologikon/gnomika"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/gnomika ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "gnomika"
4
+
5
+ Gnomika.main
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/gnomikologikon/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "gnomikologikon-fortune"
7
+ spec.version = Gnomika::Ruby::VERSION
8
+ spec.authors = ["Theodoros Grammenos"]
9
+ spec.email = ["teogramm@outlook.com"]
10
+
11
+ spec.summary = "A tool to generate cookies for the fortune command from https://gnomikologikon.gr"
12
+ spec.description = "This is a ruby application that downloads quotes from https://gnomikologikon.gr and
13
+ automatically converts them to files that can be used with the fortune command."
14
+ spec.homepage = "https://github.com/teogramm/gnomikologikon-fortune"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/teogramm/gnomikologikon-fortune"
21
+
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features|.github)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = `git ls-files -- exe/*`.split("\n").map { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "nokogiri", "~>1.10"
30
+ spec.add_dependency "httparty"
31
+ spec.add_dependency "ruby-progressbar"
32
+
33
+ spec.add_development_dependency "rdoc"
34
+ spec.add_development_dependency "rubocop", "~> 1.7"
35
+ spec.add_development_dependency "minitest", "~> 5.0"
36
+ spec.add_development_dependency "rake", "~> 13.0"
37
+ end
data/lib/gnomika.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gnomikologikon/ui"
4
+ require "gnomikologikon/arg_parser"
5
+ require "gnomikologikon/web_processing"
6
+ require "gnomikologikon/file_writer"
7
+ require "optparse"
8
+
9
+ ##
10
+ # Module containing all classes and functions of the application
11
+ module Gnomika
12
+ def self.main
13
+ # Parse command line arguments
14
+ options = ArgParser.parse(ARGV)
15
+ # Get available categories
16
+ available_categories = Gnomika.fetch_category_info
17
+ # Prompt user to select a category
18
+ selected_category = select_category(available_categories)
19
+ # Prompt user to select subcategories
20
+ selected_subcategories = select_subcategories(selected_category)
21
+ # Create a progress bar
22
+ progressbar = ProgressBar.create(total: selected_subcategories.length, title: "Download Progress")
23
+ # Get the quotes and display progress
24
+ quotes = Gnomika.get_quotes_for_categories(selected_subcategories){
25
+ progressbar.increment
26
+ }
27
+ # Create quote files with the options given as parameters
28
+ begin
29
+ write_files(options, quotes)
30
+ rescue StandardError => e
31
+ STDERR.puts e.message
32
+ end
33
+ end
34
+ end
35
+
36
+ Gnomika.main if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+
5
+ module Gnomika
6
+ ##
7
+ # Class used to parse command line arguments
8
+ class ArgParser
9
+
10
+ ##
11
+ # Contains the application options
12
+ class GnomikaOptions
13
+ attr_accessor :custom_output_dir_set, :custom_output_dir_value, :single_file, :single_file_name, :list_only
14
+
15
+ def initialize
16
+ self.custom_output_dir_set = false
17
+ self.custom_output_dir_value = ""
18
+ self.single_file = false
19
+ self.single_file_name = ""
20
+ end
21
+ end
22
+
23
+ ##
24
+ # Parses the given option into a struct
25
+ # @param arguments Array of parameters given to the program
26
+ # @return A GnomikaOptions object
27
+ def self.parse(arguments)
28
+ options = GnomikaOptions.new
29
+ parser = create_parser(options)
30
+ parser.parse(arguments)
31
+ options
32
+ end
33
+
34
+ class << self
35
+ private
36
+
37
+ ##
38
+ # Creates a parser that places values in the given object
39
+ # @param options The object to place the values in
40
+ def create_parser(options)
41
+ parser = OptionParser.new
42
+
43
+ parser.on "-o DIR", "--output-dir DIR", "Specify custom output directory" do |value|
44
+ options.custom_output_dir_set = true
45
+ options.custom_output_dir_value = value
46
+ end
47
+ parser.on "-s FILENAME", "--single-file FILENAME", "Output all quotes in a single file" do |name|
48
+ options.single_file = true
49
+ options.single_file_name = name
50
+ end
51
+ parser.on "-l", "--list-categories", "List all available categories" do
52
+ options.list_only = true
53
+ end
54
+
55
+ parser.on "-h", "--help", "Print this help text" do
56
+ puts parser
57
+ exit
58
+ end
59
+
60
+ parser.on "-v", "--version", "Print program version" do
61
+ puts "gnomikologikon-fortune #{Gnomika::Ruby::VERSION}"
62
+ exit
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,35 @@
1
+
2
+ module Gnomika
3
+ ##
4
+ # A quote category. Has many subcategories
5
+ class Category
6
+ attr_reader :subcategories, :name
7
+
8
+ ##
9
+ # @param category_name Category name
10
+ # @param subcategories Array of subcategories included in category. Is an empty array, if omitted
11
+ def initialize(category_name, subcategories: [])
12
+ @name = category_name
13
+ @subcategories = subcategories
14
+ end
15
+ end
16
+
17
+ ##
18
+ # A quote subcategory. Has a name and a URL to the quotes page.
19
+ class Subcategory
20
+ attr_reader :name, :url, :quotes
21
+
22
+ ##
23
+ # @param subcategory_name Subcategory name
24
+ # @param subcategory_url URL containing the quotes of this subcategory
25
+ def initialize(subcategory_name, subcategory_url)
26
+ @name = subcategory_name
27
+ @url = subcategory_url
28
+ end
29
+
30
+ def ==(other)
31
+ self.name == other.name
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,74 @@
1
+ module Gnomika
2
+
3
+ ##
4
+ # Writes given quotes to files according to the given options.
5
+ # @param options GnomikaOptions object containing file output options
6
+ # @param quotes Hash matching each subcategory to an Array with quotes
7
+ def self.write_files(options, quotes)
8
+ # If no custom directory is specified, use the current directory
9
+ output_directory = Dir.pwd
10
+ if options.custom_output_dir_set
11
+ custom_dir = options.custom_output_dir_value
12
+ begin
13
+ unless Dir.exist? custom_dir
14
+ Dir.mkdir custom_dir
15
+ end
16
+ output_directory = custom_dir
17
+ rescue StandardError => e
18
+ raise e
19
+ end
20
+ end
21
+ file_writer = FileWriter.new(output_directory, options.single_file, single_file_name: options.single_file_name)
22
+ quotes.each_pair do |subcategory,subcategory_quotes|
23
+ file_writer.write_quotes(subcategory.name,subcategory_quotes)
24
+ end
25
+ # Must generate strfiles for fortune command to work
26
+ file_writer.generate_strfiles
27
+ end
28
+
29
+ ##
30
+ # FileWriter is responsible for writing quotes to files
31
+ class FileWriter
32
+ ##
33
+ # @param output_directory Path to directory that the files will be stored, must already exist
34
+ # @param single_file true if all quotes will be written in a single file
35
+ # @param single_file_name Name of the single file
36
+ def initialize(output_directory, single_file, single_file_name: "gnomika")
37
+ @output_directory = output_directory
38
+ @single_file_mode = single_file
39
+ @files_written = []
40
+ # If single file output is specified, create the file now and use it in all future writes
41
+ if single_file
42
+ single_file_path = "#{output_directory}/#{single_file_name}"
43
+ @single_file = File.new(single_file_path,File::CREAT|File::TRUNC|File::WRONLY)
44
+ @files_written << single_file_path
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Writes the given quotes to the file.
50
+ # @param quotes Array of quotes to write
51
+ def write_quotes(subcategory_name, quotes)
52
+ # If single file output is used, use the existing file. Otherwise create a new one for this category
53
+ file = if @single_file_mode
54
+ @single_file
55
+ else
56
+ # Write filename in files_written array
57
+ new_file_name = "#{@output_directory}/#{subcategory_name}"
58
+ @files_written << new_file_name
59
+ File.new(new_file_name,File::CREAT|File::TRUNC|File::WRONLY)
60
+ end
61
+ # Write quotes separated by "%" sign
62
+ file.write(quotes.join("\n%\n"))
63
+ end
64
+
65
+ ##
66
+ # Runs the strfile command for every written file.
67
+ def generate_strfiles
68
+ until @files_written.empty?
69
+ file_path = @files_written.shift
70
+ system("strfile",file_path)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,16 @@
1
+ module Gnomika
2
+ class Quote
3
+ attr_reader :content, :author
4
+
5
+ def initialize(content, author)
6
+ @content = content
7
+ @author = author
8
+ end
9
+
10
+ def to_s
11
+ str = "#{@content}"
12
+ str << "\n - #{@author}" unless @author.strip.empty?
13
+ str
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,111 @@
1
+ module Gnomika
2
+ ##
3
+ # Prompt the user to select a category
4
+ # @param categories Array of Category objects
5
+ # @return The Category object the user selected
6
+ def self.select_category(categories)
7
+ # Index starting from 1
8
+ categories.each_with_index { |category, index| puts "#{index+1}. #{category.name}" }
9
+ ok = false
10
+ num = -1
11
+ until ok
12
+ print "Select a category: "
13
+ STDOUT.flush
14
+ input = STDIN.gets
15
+ begin
16
+ # Remove 1 because display indexes starting from 1.
17
+ num = Integer(input) - 1
18
+ break unless num < 0 || num > categories.length-1
19
+ # Input was number but out of bounds
20
+ puts "Invalid selection!"
21
+ rescue ArgumentError
22
+ # Input could not be converted to integer
23
+ puts "Invalid selection!"
24
+ end
25
+ end
26
+ # Return Category object chosen by the user
27
+ categories[num]
28
+ end
29
+
30
+ ##
31
+ # Prompt user to select subcategories. Selection can be a range e.g. 1-2, single categories e.g. 1,2,3 or both
32
+ # e.g 1,2-4
33
+ # @param category The Category that will be shown
34
+ # @return Array of selected Subcategory objects
35
+ def self.select_subcategories(category)
36
+ subcategories = category.subcategories
37
+ # Index starting from 1
38
+ subcategories.each_with_index { |subcategory, index| puts "#{index+1}. #{subcategory.name}" }
39
+ puts "You can select multiple categories separated by ',' or a range of categories with '-'."
40
+
41
+ # Contains indexes of all subcategories specified by the user
42
+ selected_indexes = []
43
+ ok = false
44
+ until ok
45
+ print "Select subcategories: "
46
+ # On each loop we assume input is ok until an error is encountered
47
+ ok = true
48
+ # Empty the indexes array, in case of leftovers from previous loop
49
+ selected_indexes = []
50
+ input = STDIN.gets
51
+ # Split the input
52
+ input = input.split(",")
53
+ # Check each selection
54
+ input.each do |selection|
55
+ begin
56
+ # We need to remove 1 from each because during selection category numbers started from 1
57
+ selected_indexes += selection_to_array(selection, subcategories.length).map{|it| it - 1}
58
+ rescue => error
59
+ ok = false
60
+ puts error.message
61
+ break
62
+ end
63
+ end
64
+ end
65
+ # Remove any duplicate indexes
66
+ selected_indexes.uniq!
67
+ # Create an array with the corresponding Subcategory objects and return it
68
+ selected_indexes.map { |index| subcategories[index]}
69
+ end
70
+
71
+ private
72
+
73
+ ##
74
+ # Converts given selection into an array of indexes.
75
+ # Throws an ArgumentError if the selection is invalid. An error message is included in the exception.
76
+ # This function must be used with a single selection (e.g 3 or 3-5), not with a list of many selections (e.g 1,2,3...)
77
+ # @param selection String of the selection
78
+ # @param max_available_index Used w
79
+ # @return Array of indexes included in the selection
80
+ def self.selection_to_array(selection, max_available_index)
81
+ # Check if selection is a range
82
+ if selection.include?("-")
83
+ # Try to process it as a range
84
+ range_start, range_end = selection.split("-")
85
+ begin
86
+ range_start = Integer(range_start)
87
+ range_end = Integer(range_end)
88
+ # Check if range is correct. Start must be smaller or equal than end and end must be smaller or equal
89
+ # to max_available index
90
+ if range_start > range_end || range_end > max_available_index || range_start < 1
91
+ raise ArgumentError
92
+ end
93
+ return (range_start..range_end).to_a
94
+ rescue ArgumentError
95
+ raise ArgumentError.new "Invalid range! (#{selection.strip})"
96
+ end
97
+ else
98
+ # Assume selection is an integer
99
+ begin
100
+ number = Integer(selection)
101
+ # Check limits
102
+ if number < 1 || number > max_available_index
103
+ raise ArgumentError
104
+ end
105
+ return [number]
106
+ rescue
107
+ raise ArgumentError.new "Invalid selection! (#{selection.strip})"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gnomika
4
+ module Ruby
5
+ VERSION = "1.0.1"
6
+ end
7
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "httparty"
5
+ require "gnomikologikon/category"
6
+ require "gnomikologikon/quote"
7
+ require "ruby-progressbar"
8
+
9
+ module Gnomika
10
+ ##
11
+ # Gets information about all categories from the website
12
+ # @return An Array of Category objects
13
+ def self.fetch_category_info
14
+ response = HTTParty.get('https://www.gnomikologikon.gr/categ.php', {
15
+ headers: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"},
16
+ })
17
+
18
+ doc = Nokogiri::HTML.parse(response.body)
19
+ # Each "big" category is stored in a table with class "authrst"
20
+ category_tables = doc.xpath("//table[@class='authrst']")
21
+
22
+ categories = []
23
+
24
+ category_tables.each do |table|
25
+ # Get category name. Category names are stored in td elements with class "authrsh"
26
+ category_name = table.xpath("tr/td[@class='authrsh']").text
27
+
28
+ # Get the subcategories of each category
29
+ subcategories = []
30
+ # Subcategories of each category are a elements in a list
31
+ subcategory_elements = table.xpath("tr//ul//li//a")
32
+ subcategory_elements.each do |element|
33
+ subcategory_name = element.content
34
+ # Need to prefix category URLs with the website URL
35
+ subcategory_url = "https://www.gnomikologikon.gr/#{element[:href]}"
36
+ subcategories << Subcategory.new(subcategory_name,subcategory_url)
37
+ end
38
+
39
+ categories << Category.new(category_name,subcategories: subcategories)
40
+ end
41
+ categories
42
+ end
43
+
44
+ ##
45
+ # Get all quotes for the given subcategories
46
+ # @yield Runs the given block after each subcategory is processed
47
+ # @param subcategories Array of subcategories
48
+ # @return Hash matching each subcategory to an Array of quotes
49
+ def self.get_quotes_for_categories(subcategories)
50
+ quotes = {}
51
+ subcategories.each do |subcategory|
52
+ response = HTTParty.get(subcategory.url, {
53
+ headers: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"},
54
+ })
55
+ quotes[subcategory] = get_quotes_from_html(response.body)
56
+ yield if block_given?
57
+ # Throttle the connection because processing is fast
58
+ sleep 1
59
+ end
60
+ quotes
61
+ end
62
+
63
+ ##
64
+ # Fetch all quotes from given HTML page
65
+ # @param page_body Body of HTML page to extract quotes from
66
+ # @return Array of quotes
67
+ def self.get_quotes_from_html(page_body)
68
+ doc = Nokogiri::HTML.parse(page_body)
69
+ quotes_tables = doc.xpath("//table[@class='quotes']//td[@class='quote']")
70
+ quotes = []
71
+ quotes_tables.each do |quote|
72
+ # Get quote contents
73
+ content = quote.at_xpath("./text()").text
74
+ # Check if there is an explanation
75
+ explanation = quote.xpath("./table[@class='expth']//td")
76
+ unless explanation.empty?
77
+ # If an explanation exists, there are two td elements
78
+ # Remove pavla
79
+ explanation = explanation.reject{|element| element["class"] == "pavla"}
80
+ # Keep the explanation td element
81
+ explanation = explanation[0]
82
+ content << "\n(#{explanation.text})"
83
+ end
84
+ # Check if there is a comment
85
+ comment = quote.xpath("./p[contains(@class, 'comnt')]")
86
+ unless comment.nil?
87
+ # Do not add comment if it does not contain text
88
+ content << "\n#{comment.text}" unless comment.text.empty?
89
+ end
90
+ # HTML p elements with class auth0-auth4 contain quote author and additional information (e.g. book)
91
+ # Get the text of all auth p elements and combine them in a string
92
+ author = quote.xpath(".//p[contains(@class, 'auth')]")
93
+ author = author.map{|el| el.text}.join(' ')
94
+ quotes << Quote.new(content,author)
95
+ end
96
+ quotes
97
+ end
98
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gnomikologikon-fortune
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Theodoros Grammenos
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
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: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ description: |-
112
+ This is a ruby application that downloads quotes from https://gnomikologikon.gr and
113
+ automatically converts them to files that can be used with the fortune command.
114
+ email:
115
+ - teogramm@outlook.com
116
+ executables:
117
+ - gnomika
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - ".gitignore"
122
+ - ".rubocop.yml"
123
+ - Gemfile
124
+ - Gemfile.lock
125
+ - LICENSE
126
+ - README.md
127
+ - Rakefile
128
+ - bin/console
129
+ - bin/setup
130
+ - exe/gnomika
131
+ - gnomikologikon-fortune.gemspec
132
+ - lib/gnomika.rb
133
+ - lib/gnomikologikon/arg_parser.rb
134
+ - lib/gnomikologikon/category.rb
135
+ - lib/gnomikologikon/file_writer.rb
136
+ - lib/gnomikologikon/quote.rb
137
+ - lib/gnomikologikon/ui.rb
138
+ - lib/gnomikologikon/version.rb
139
+ - lib/gnomikologikon/web_processing.rb
140
+ homepage: https://github.com/teogramm/gnomikologikon-fortune
141
+ licenses: []
142
+ metadata:
143
+ allowed_push_host: https://rubygems.org/
144
+ homepage_uri: https://github.com/teogramm/gnomikologikon-fortune
145
+ source_code_uri: https://github.com/teogramm/gnomikologikon-fortune
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 2.4.0
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubygems_version: 3.1.4
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: A tool to generate cookies for the fortune command from https://gnomikologikon.gr
165
+ test_files: []