wgit 0.10.8 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66e8b435303d07b2f81d260badc96662936599c9782916f7f014b74a7c617499
4
- data.tar.gz: 7b55890c66ec09efd8d5749bd66605a4cb43d5091416f072f8fcc5aaaa85fbe7
3
+ metadata.gz: 4dee43af6274102c9bc6ad4f32c8811f57c5dc2833e923e038aac8f7f2072385
4
+ data.tar.gz: 9463768a40c78ab9c91ac34dd1c3a0fd1e7b990440b70766feebb9e2f0f99bd4
5
5
  SHA512:
6
- metadata.gz: fe1b605224f6682ac504f17b55ab83518556f1320f0410741af8f95bf3a669918c69b48832fb413ca1f78482fdbb7e0d2e7d6f57841c6a562b7f926f7511cdd7
7
- data.tar.gz: 856be2111709bc96488b7d43abbc49c563a9a56330344adb4b9ec40fc263cb91e63465c3c3dab317c0d8930965a609a43102d53d80bbc2001e6165a15cb905fa
6
+ metadata.gz: 5c94fcae3a56254a6c0d9d67597f1a2125439c1ee3d7d68e22fb70fa59298735d76b0fb8e77bc44b0850f6ba561fe11df3a867973d5f0533adddda9d2c6f2002
7
+ data.tar.gz: 779bf20dc1eaa29cc926a836d5a5a155c2270db1be0d22bc52ba9893cbd8d3aaec2cee7810b660fb9014020f5de2290af2b23680cc040a9c682ca82431d6f50d
data/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Wgit Change Log
2
2
 
3
- ## v0.0.0 (TEMPLATE - DO NOT EDIT)
3
+ ## v0.0.0 [- BREAKING CHANGES] (TEMPLATE - DO NOT EDIT)
4
4
  ### Added
5
5
  - ...
6
6
  ### Changed/Removed
@@ -9,6 +9,77 @@
9
9
  - ...
10
10
  ---
11
11
 
12
+ ## v0.12.0 - BREAKING CHANGES
13
+ A big release with several breaking changes, not all of which can be listed below. The headline features for this release are the introduction of a database adapter, allowing Wgit to work with practically any underlying database system; and a custom in-house text extractor.
14
+ ### Added
15
+ - `Wgit::Database::DatabaseAdapter` class to allow the easy development of other database adapters that work with Wgit.
16
+ - `Wgit::Database::InMemory` database adapter class and logic for quick and easy usage of Wgit on the fly (without needing a MongoDB instance to be running). This also serves as an example of how to write your own database adapter class.
17
+ - `Wgit::HTMLToText` class and logic for extracting text from a HTML string. This is now how a `Wgit::Document` extracts its text for searching etc. This class is different in that it not only focuses on the elements we specify, but also applies those elements display in how it delimits where one sentence ends and the next starts e.g. `<span>` is `:inline` etc.
18
+ - `Wgit::Document#nearest_fragment` method which allows you to search for the nearest fragement (#blah) to a piece of text and/or element type.
19
+ - `Wgit::Model.search_fields` and `.set_search_fields` for setting which search fields are used in document and database adapter searches.
20
+ - `Wgit::Model.include_doc_html` and `include_doc_score` methods to set in one place if html/score is included in the corresponding document model output.
21
+ - `Wgit::Assertable#common_arr_types` method.
22
+ - `Wgit::Utils.pprint(display:)` param to turn off all logs easily (by setting from ENV etc).
23
+ - `Wgit::Crawler#crawl_site(max_pages:)` param to quit the crawl early.
24
+ - `Wgit::Utils.pprint_search_results(include_score:)` param to print the score of each result.
25
+ - `Wgit::Utils.pprint_all_search_results` method to print all matches for each result.
26
+ - `Wgit::DSL#search` params `include_score:` and `top_result_only:`.
27
+ ### Changed/Removed
28
+ - Update Wgit to work with ruby v3 and above, removed support for older versions.
29
+ - Removed default extractors `meta_robots` and `meta_wgit` without losing any functionality.
30
+ - `Wgit::Database::Model` is now moved up a level to become `Wgit::Model`.
31
+ - `Wgit::Database` is now `Wgit::Database::MongoDB` and is alised to `Wgit::Database.adapter_class`.
32
+ - Renamed any `Wgit::Database` and `Wgit::DSL` methods called `#clear_*` to be `#empty_*`.
33
+ - Moved `Wgit::MongoDB#search!` to `Wgit::DSL#search`.
34
+ - Renamed `Wgit::MongoDB#search_text` to `Wgit::Database::MongoDB#search!`.
35
+ - Reworked `Wgit::DSL` methods to accept a database param instead of a connection_string. This offers better flexibility.
36
+ - Updated the Wgit `Dockerfile` to pull from `mongo:latest`.
37
+ - `Wgit::Document#search` now searches more than just it's text. See `Wgit::Model.search_fields` for more information.
38
+ - `Wgit::Document` now has the following search methods: `#search`, `#search_text`, `#search_text!`. See their documentation and source code for more information.
39
+ - `Wgit::Document.define_extractor` now accepts a `nil` xpath parameter which omits the xpath search.
40
+ - `Wgit::Indexer#index_*` methods now use `:skip` block return value to avoid DB upserts.
41
+ - Updated `Wgit::Crawler.new` to accept `ferrum_opts:` which are passed to directly ferrum on init.
42
+ - `Wgit::Document#search` now accepts a `&block` which yields the results Hash.
43
+ - Updated `Wgit::Indexer#index_*` methods to accept `ENV["WGIT_IGNORE_ROBOTS_TXT"]` ignoring a site's robot.txt file.
44
+ ### Fixed
45
+ - Issue [Document#search only searches the text](https://github.com/michaeltelford/wgit/issues/2)
46
+ - Issue [Document#search doesn't find certain text](https://github.com/michaeltelford/wgit/issues/20)
47
+ - Issue with `Wgit::Document#search(whole_sentence:)` search capability by updating the regex used.
48
+ - Issue with `Wgit::Document#search` which now totals the text score for duplicated results.
49
+ ---
50
+
51
+ ## v0.11.0 - BREAKING CHANGES
52
+ This release is a biggie with the main headline being the introduction of robots.txt support (see below). This release introduces several breaking changes so take care when updating your current version of Wgit.
53
+ ### Added
54
+ - Ability to prevent indexing via `robots.txt` and `noindex` values in HTML `meta` elements and HTTP response header `X-Robots-Tag`. See new class `Wgit::RobotsParser` and the updated `Wgit::Indexer#index_*` methods. Also see the [wiki article](https://github.com/michaeltelford/wgit/wiki/How-To-Prevent-Indexing) on the subject.
55
+ - `Wgit::RobotsParser` class for parsing `robots.txt` files.
56
+ - `Wgit::Response#no_index?` and `Wgit::Document#no_index?` methods (see wiki article above).
57
+ - Added two new default extractors which extract robots meta elements for use in `Wgit::Document#no_index?`.
58
+ - Added `Wgit::Document.to_h_ignore_vars` Array for user manipulation.
59
+ - Added `Wgit::Utils.pprint` method to aid debugging.
60
+ - Added `Wgit::Utils.sanitize_url` method.
61
+ - Added `Wgit::Indexer#index_www(max_urls_per_iteration:, ...)` param.
62
+ - Added `Wgit::Url#redirects` and `#redirects=` methods.
63
+ - Added `Wgit::Url#redirects_journey` used by `Wgit::Indexer` to insert a Url and it's redirects.
64
+ - Added `Wgit::Database#bulk_upsert` which `Wgit::Indexer` now uses where possible. This reduces the total database calls made during an index operation.
65
+ ### Changed/Removed
66
+ - Updated `Wgit::Indexer#index_*` methods to honour index prevention methods (see the [wiki article](https://github.com/michaeltelford/wgit/wiki/How-To-Prevent-Indexing)).
67
+ - Updated `Wgit::Utils.sanitize*` methods so they no longer modify the receiver.
68
+ - Updated `Wgit::Crawler#crawl_url` to always return the crawled `Wgit::Document`. If relying on `nil` in your code, you should now use `doc.empty?` instead.
69
+ - Updated `Wgit::Indexer` method logs.
70
+ - Updated/added custom class `#inspect` methods.
71
+ - Renamed `Wgit::Utils.printf_search_results` to `pprint_search_results`.
72
+ - Renamed `Wgit::Url#concat` to `#join`. The `#concat` method is now `String#concat`.
73
+ - Updated `Wgit::Indexer` methods to now write external Urls to the Database as: `doc.external_urls.map(&:to_origin)` meaning `http://example.com/about` becomes `http://example.com`.
74
+ - Updated the following methods to no longer omit trailing slashes from Urls: `Wgit::Url` - `#to_path`, `#omit_base`, `#omit_origin` and `Wgit::Document` - `#internal_links`, `#internal_absolute_links`, `#external_links`. For an average website, this results in ~30% less network requests when crawling.
75
+ - Updated Ruby version to `3.3.0`.
76
+ - Updated all bundle dependencies to latest versions, see `Gemfile.lock` for exact versions.
77
+ ### Fixed
78
+ - `Wgit::Crawler#crawl_site` now internally records all redirects for a given Url.
79
+ - `Wgit::Crawler#crawl_site` infinite loop when using Wgit on a Ruby version > `3.0.2`.
80
+ - Various other minor fixes/improvements throughout the code base.
81
+ ---
82
+
12
83
  ## v0.10.8
13
84
  ### Added
14
85
  - Custom `#inspect` methods to `Wgit::Url` and `Wgit::Document` classes.
data/CODE_OF_CONDUCT.md CHANGED
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
55
55
  ## Enforcement
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at michael.telford@live.com. All
58
+ reported by contacting the project team on Github. All
59
59
  complaints will be reviewed and investigated and will result in a response that
60
60
  is deemed necessary and appropriate to the circumstances. The project team is
61
61
  obligated to maintain confidentiality with regard to the reporter of an incident.
data/CONTRIBUTING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Consult
4
4
 
5
- Before you make a contribution, reach out to michael.telford@live.com about what changes need made. Otherwise, your time spent might be wasted. Once you're clear on what needs done follow the technical steps below.
5
+ Before you make a contribution, reach out on Github about what changes need made. Otherwise, your time spent might be wasted. Once you're clear on what needs done follow the technical steps below.
6
6
 
7
7
  ## Technical Steps
8
8
 
@@ -12,7 +12,7 @@ Before you make a contribution, reach out to michael.telford@live.com about what
12
12
  - Write some code
13
13
  - Re-run the tests (which now hopefully pass)
14
14
  - Push your branch to your `origin` remote
15
- - Open a GitHub Pull Request (with the target branch being wgit's `origin/master`)
15
+ - Open a GitHub Pull Request (with the target branch as wgit's (upstream) `master`)
16
16
  - Apply any requested changes
17
17
  - Wait for your PR to be merged
18
18
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Inline gem version](https://badge.fury.io/rb/wgit.svg)](https://rubygems.org/gems/wgit)
4
4
  [![Inline downloads](https://img.shields.io/gem/dt/wgit)](https://rubygems.org/gems/wgit)
5
- [![Inline build](https://travis-ci.org/michaeltelford/wgit.svg?branch=master)](https://travis-ci.org/michaeltelford/wgit)
5
+ [![Inline build](https://github.com/michaeltelford/wgit/actions/workflows/wgit.yaml/badge.svg?branch=master)](https://github.com/michaeltelford/wgit/actions)
6
6
  [![Inline docs](http://inch-ci.org/github/michaeltelford/wgit.svg?branch=master)](http://inch-ci.org/github/michaeltelford/wgit)
7
7
  [![Inline code quality](https://api.codacy.com/project/badge/Grade/d5a0de62e78b460997cb8ce1127cea9e)](https://www.codacy.com/app/michaeltelford/wgit?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=michaeltelford/wgit&amp;utm_campaign=Badge_Grade)
8
8
 
@@ -13,12 +13,12 @@ Wgit is a HTML web crawler, written in Ruby, that allows you to programmatically
13
13
  Wgit was primarily designed to crawl static HTML websites to index and search their content - providing the basis of any search engine; but Wgit is suitable for many application domains including:
14
14
 
15
15
  - URL parsing
16
- - Document content extraction (data mining)
17
- - Crawling entire websites (statistical analysis)
16
+ - Document content extraction (data mining etc)
17
+ - Recursive website crawling (indexing, statistical analysis etc)
18
18
 
19
19
  Wgit provides a high level, easy-to-use API and DSL that you can use in your own applications and scripts.
20
20
 
21
- Check out this [demo search engine](https://wgit-search-engine.fly.dev) - [built](https://github.com/michaeltelford/search_engine) using Wgit and Sinatra - deployed to [fly.io](https://fly.io). Try searching for something that's Ruby related like "Matz" or "Rails".
21
+ Check out this [demo search engine](https://wgit-search-engine.fly.dev) - [built](https://github.com/michaeltelford/search_engine) using Wgit, Sinatra and MongoDB - deployed to [fly.io](https://fly.io). Try searching for something that's Ruby related like "Matz" or "Rails".
22
22
 
23
23
  ## Table Of Contents
24
24
 
@@ -78,7 +78,7 @@ Which outputs:
78
78
  ]
79
79
  ```
80
80
 
81
- Great! But what if we want to crawl and store the content in a database, so that it can be searched? Wgit makes it easy to index and search HTML using [MongoDB](https://www.mongodb.com/):
81
+ Great! But what if we want to crawl and store the content in a database, so that it can be searched? Wgit makes it easy to index and search HTML using [MongoDB](https://www.mongodb.com/) (by default):
82
82
 
83
83
  ```ruby
84
84
  require 'wgit'
@@ -87,7 +87,7 @@ include Wgit::DSL
87
87
 
88
88
  Wgit.logger.level = Logger::WARN
89
89
 
90
- connection_string 'mongodb://user:password@localhost/crawler'
90
+ ENV['WGIT_CONNECTION_STRING'] = 'mongodb://user:password@localhost/crawler'
91
91
 
92
92
  start 'http://quotes.toscrape.com/tag/humor/'
93
93
  follow "//li[@class='next']/a/@href"
@@ -109,11 +109,11 @@ http://quotes.toscrape.com/tag/humor/page/2/
109
109
  ...
110
110
  ```
111
111
 
112
- Using a MongoDB [client](https://robomongo.org/), we can see that the two web pages have been indexed, along with their extracted *quotes* and *authors*:
112
+ Using a database client, we can see that the two web pages have been indexed, along with their extracted *quotes* and *authors*:
113
113
 
114
114
  ![MongoDBClient](https://raw.githubusercontent.com/michaeltelford/wgit/assets/assets/wgit_mongo_index.png)
115
115
 
116
- The [DSL](https://github.com/michaeltelford/wgit/wiki/How-To-Use-The-DSL) makes it easy to write scripts for experimenting with. Wgit's DSL is simply a wrapper around the underlying classes however. For comparison, here is the above example written using the Wgit API *instead of* the DSL:
116
+ The [DSL](https://github.com/michaeltelford/wgit/wiki/How-To-Use-The-DSL) makes it easy to write scripts for experimenting with. Wgit's DSL is simply a wrapper around the underlying classes. For comparison, here is the quote example re-written using the Wgit API *instead of* the DSL:
117
117
 
118
118
  ```ruby
119
119
  require 'wgit'
@@ -143,9 +143,9 @@ puts JSON.generate(quotes)
143
143
  There are many [other HTML crawlers](https://awesome-ruby.com/#-web-crawling) out there so why use Wgit?
144
144
 
145
145
  - Wgit has excellent unit testing, 100% documentation coverage and follows [semantic versioning](https://semver.org/) rules.
146
- - Wgit excels at crawling an entire website's HTML out of the box. Many alternative crawlers require you to provide the `xpath` needed to *follow* the next URLs to crawl. Wgit by default, crawls the entire site by extracting its internal links pointing to the same host.
146
+ - Wgit excels at crawling an entire website's HTML out of the box. Many alternative crawlers require you to provide the `xpath` needed to *follow* the next URLs to crawl. Wgit by default, crawls the entire site by extracting its internal links pointing to the same host - no `xpath` needed.
147
147
  - Wgit allows you to define content *extractors* that will fire on every subsequent crawl; be it a single URL or an entire website. This enables you to focus on the content you want.
148
- - Wgit can index (crawl and store) HTML to a database making it a breeze to build custom search engines. You can also specify which page content gets searched, making the search more meaningful. For example, here's a script that will index the Wgit [wiki](https://github.com/michaeltelford/wgit/wiki) articles:
148
+ - Wgit can index (crawl and save) HTML to a database making it a breeze to build custom search engines. You can also specify which page content gets searched, making the search more meaningful. For example, here's a script that will index the Wgit [wiki](https://github.com/michaeltelford/wgit/wiki) articles:
149
149
 
150
150
  ```ruby
151
151
  require 'wgit'
@@ -164,6 +164,9 @@ indexer = Wgit::Indexer.new
164
164
  indexer.index_site(wiki, **opts)
165
165
  ```
166
166
 
167
+ - Wgit supports different databases through the use of "adapter" classes, which you can write to support your own database of choice.
168
+ - Wgit's built in indexing methods will by default, honour a site's `robots.txt` rules. There's also a handy `robots.txt` parser that you can use in your own code.
169
+
167
170
  ## Why Not Wgit?
168
171
 
169
172
  So why might you not use Wgit, I hear you ask?
@@ -176,9 +179,9 @@ So why might you not use Wgit, I hear you ask?
176
179
 
177
180
  Only MRI Ruby is tested and supported, but Wgit may work with other Ruby implementations.
178
181
 
179
- Currently, the required MRI Ruby version is:
182
+ Currently, the supported range of MRI Ruby versions is:
180
183
 
181
- `ruby '>= 2.6', '< 4'`
184
+ `ruby '~> 3.0'` a.k.a. between Ruby 3.0 and up to but not including Ruby 4.0. Wgit will probably work fine with older versions but best to upgrade if possible.
182
185
 
183
186
  ### Using Bundler
184
187
 
@@ -208,10 +211,11 @@ Installing the Wgit gem adds a `wgit` executable to your `$PATH`. The executable
208
211
  The `wgit` executable does the following things (in order):
209
212
 
210
213
  1. `require wgit`
211
- 2. `eval`'s a `.wgit.rb` file (if one exists in either the local or home directory, which ever is found first)
212
- 3. Starts an interactive shell (using `pry` if it's installed, or `irb` if not)
214
+ 2. Loads an `.env` file (if one exists in either the local or home directory, which ever is found first)
215
+ 3. `eval`'s a `.wgit.rb` file (if one exists in either the local or home directory, which ever is found first)
216
+ 4. Starts an interactive shell (using `pry` if it's installed, or `irb` if not)
213
217
 
214
- The `.wgit.rb` file can be used to seed fixture data or define helper functions for the session. For example, you could define a function which indexes your website for quick and easy searching everytime you start a new session.
218
+ The `.wgit.rb` file can be used to seed fixture data or define helper functions for the session. For example, you could define a function which indexes your website for quick and easy searching everytime you start `wgit`.
215
219
 
216
220
  ## License
217
221
 
@@ -237,14 +241,14 @@ And you're good to go!
237
241
 
238
242
  ### Tooling
239
243
 
240
- Wgit uses the [`toys`](https://github.com/dazuma/toys) gem (instead of Rake) for task invocation. For a full list of available tasks a.k.a. tools, run `toys --tools`. You can search for a tool using `toys -s tool_name`. The most commonly used tools are listed below...
244
+ Wgit uses the [toys](https://github.com/dazuma/toys) gem (instead of Rake) for task invocation. Always run `toys` as `bundle exec toys`. For a full list of available tasks a.k.a. tools, run `bundle exec toys --tools`. You can search for a tool using `bundle exec toys -s tool_name`. The most commonly used tools are listed below...
241
245
 
242
- Run `toys db` to see a list of database related tools, enabling you to run a Mongo DB instance locally using Docker. Run `toys test` to execute the tests.
246
+ Run `bundle exec toys db` to see a list of database related tools, enabling you to run a Mongo DB instance locally using Docker. Run `bundle exec toys test` to execute the tests.
243
247
 
244
- To generate code documentation locally, run `toys yardoc`. To browse the docs in a browser run `toys yardoc --serve`. You can also use the `yri` command line tool e.g. `yri Wgit::Crawler#crawl_site` etc.
248
+ To generate code documentation locally, run `bundle exec toys yardoc`. To browse the docs in a browser run `bundle exec toys yardoc --serve`. You can also use the `yri` command line tool e.g. `yri Wgit::Crawler#crawl_site` etc.
245
249
 
246
- To install this gem onto your local machine, run `toys install` and follow the prompt.
250
+ To install this gem onto your local machine, run `bundle exec toys install` and follow the prompt.
247
251
 
248
252
  ### Console
249
253
 
250
- You can run `toys console` for an interactive shell using the `./bin/wgit` executable. The `toys setup` task will have created an `.env` and `.wgit.rb` file which get loaded by the executable. You can use the contents of this [gist](https://gist.github.com/michaeltelford/b90d5e062da383be503ca2c3a16e9164) to turn the executable into a development console. It defines some useful functions, fixtures and connects to the database etc. Don't forget to set the `WGIT_CONNECTION_STRING` in the `.env` file.
254
+ You can run `bundle exec toys console` for an interactive shell using the `./bin/wgit` executable. The `bundle exec toys setup` task will have created an `.env` and `.wgit.rb` file which get loaded by the executable. You can use the contents of this [gist](https://gist.github.com/michaeltelford/b90d5e062da383be503ca2c3a16e9164) to turn the executable into a development console. It defines some useful functions, fixtures and connects to the database etc. Don't forget to set the `WGIT_CONNECTION_STRING` in the `.env` file.
data/bin/wgit CHANGED
@@ -1,39 +1,95 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'wgit'
3
+ require "wgit"
4
+
5
+ # Shorten the filepath for nicer output to the user.
6
+ def format_path(dir, path)
7
+ return path.gsub(dir, ".") if dir == Dir.pwd
8
+ return path.gsub(dir, "~") if dir == Dir.home
9
+
10
+ path
11
+ end
12
+
13
+ # Load .env file (if it exists somewhere).
14
+ def load_env
15
+ begin
16
+ require "dotenv"
17
+ rescue LoadError
18
+ puts "Skipping .env load because 'dotenv' isn't installed"
19
+ puts
20
+ return false
21
+ end
22
+
23
+ puts "Searching for .env file in local and home directories..."
24
+ success = false
25
+
26
+ [Dir.pwd, Dir.home].each do |dir|
27
+ path = "#{dir}/.env"
28
+ next unless File.exist?(path)
29
+
30
+ puts "Loading #{format_path(dir, path)}"
31
+ puts "Call `load_env` after changes to re-load the environment variables"
32
+
33
+ Dotenv.load(path)
34
+ success = true
35
+
36
+ break
37
+ end
38
+
39
+ puts
40
+
41
+ success
42
+ end
4
43
 
5
44
  # Eval .wgit.rb file (if it exists somewhere).
6
- def eval_wgit(filepath = nil)
7
- puts 'Searching for .wgit.rb file in local and home directories...'
45
+ def eval_wgit
46
+ puts "Searching for .wgit.rb file in local and home directories..."
47
+ success = false
8
48
 
9
- [filepath, Dir.pwd, Dir.home].each do |dir|
49
+ [Dir.pwd, Dir.home].each do |dir|
10
50
  path = "#{dir}/.wgit.rb"
11
51
  next unless File.exist?(path)
12
52
 
13
- puts "Eval'ing #{path}"
14
- puts 'Call `eval_wgit` after changes to re-eval the file'
53
+ puts "Eval'ing #{format_path(dir, path)}"
54
+ puts "Call `eval_wgit` after changes to re-eval the file"
55
+
15
56
  eval(File.read(path))
57
+ success = true
16
58
 
17
59
  break
18
60
  end
19
61
 
20
- nil
21
- end
62
+ puts
22
63
 
23
- eval_wgit
24
- puts "\n#{Wgit.version_str}\n\n"
64
+ success
65
+ end
25
66
 
67
+ # Choose and return which REPL class to use.
26
68
  # Use Pry if installed or fall back to IRB.
27
- begin
28
- require 'pry'
29
- klass = Pry
30
- rescue LoadError
31
- require 'irb'
32
- klass = IRB
33
-
34
- puts "Starting IRB because Pry isn't installed."
69
+ def repl_class
70
+ begin
71
+ require "pry"
72
+ klass = Pry
73
+ rescue LoadError
74
+ require "irb"
75
+ klass = IRB
76
+
77
+ puts "Using 'irb' REPL because 'pry' isn't installed"
78
+ puts
79
+ end
80
+
81
+ klass
35
82
  end
36
83
 
84
+ ### START OF EXECUTABLE ###
85
+
86
+ load_env
87
+ eval_wgit
88
+ klass = repl_class
89
+
90
+ puts Wgit.version_str
91
+ puts "#{'-' * Wgit.version_str.size}\n\n"
92
+
37
93
  klass.start
38
94
 
39
- puts 'Interactive session complete.'
95
+ puts "Interactive wgit session complete"
@@ -4,11 +4,18 @@ module Wgit
4
4
  # Module containing assertion methods including type checking and duck typing.
5
5
  module Assertable
6
6
  # Default type fail message.
7
- DEFAULT_TYPE_FAIL_MSG = 'Expected: %s, Actual: %s'
7
+ DEFAULT_TYPE_FAIL_MSG = "Expected: %s, Actual: %s"
8
+
8
9
  # Wrong method message.
9
- NON_ENUMERABLE_MSG = 'Expected an Enumerable responding to #each, not: %s'
10
+ NON_ENUMERABLE_MSG = "Expected an Enumerable responding to #each, not: %s"
11
+
12
+ # Enumerable with more than one type across it's elements.
13
+ MIXED_ENUMERABLE_MSG = "Expected an Enumerable with elements of a single \
14
+ common type"
15
+
10
16
  # Default duck fail message.
11
17
  DEFAULT_DUCK_FAIL_MSG = "%s doesn't respond_to? %s"
18
+
12
19
  # Default required keys message.
13
20
  DEFAULT_REQUIRED_KEYS_MSG = "Some or all of the required keys are not \
14
21
  present: %s"
@@ -47,6 +54,25 @@ present: %s"
47
54
  arr.each { |obj| assert_types(obj, type_or_types, msg) }
48
55
  end
49
56
 
57
+ # All objects within arr must match one of the types listed in
58
+ # type_or_types; or an exception is raised using msg, if provided.
59
+ # Ancestors of the same type are allowed and considered common.
60
+ #
61
+ # @param arr [Enumerable#each] Enumerable of objects to type check.
62
+ # @param type_or_types [Type, Array<Type>] The allowed type(s).
63
+ # @param msg [String] The raised StandardError message, if provided.
64
+ # @raise [StandardError] If the assertion fails.
65
+ # @return [Object] The given arr on successful assertion.
66
+ def assert_common_arr_types(arr, type_or_types, msg = nil)
67
+ raise format(NON_ENUMERABLE_MSG, arr.class) unless arr.respond_to?(:each)
68
+
69
+ type = arr.first.class
70
+ type_match = arr.all? { |obj| type.ancestors.include?(obj.class) }
71
+ raise MIXED_ENUMERABLE_MSG unless type_match
72
+
73
+ assert_arr_types(arr, type_or_types, msg)
74
+ end
75
+
50
76
  # The obj_or_objs must respond_to? all of the given methods or an
51
77
  # Exception is raised using msg, if provided.
52
78
  #
@@ -75,7 +101,7 @@ present: %s"
75
101
  # @raise [KeyError] If the assertion fails.
76
102
  # @return [Hash] The given hash on successful assertion.
77
103
  def assert_required_keys(hash, keys, msg = nil)
78
- msg ||= format(DEFAULT_REQUIRED_KEYS_MSG, keys.join(', '))
104
+ msg ||= format(DEFAULT_REQUIRED_KEYS_MSG, keys.join(", "))
79
105
  all_present = keys.all? { |key| hash.keys.include? key }
80
106
  raise KeyError, msg unless all_present
81
107
 
@@ -86,7 +112,7 @@ present: %s"
86
112
 
87
113
  # obj must respond_to? all methods or an exception is raised.
88
114
  def _assert_respond_to(obj, methods, msg = nil)
89
- raise 'methods must respond_to? :all?' unless methods.respond_to?(:all?)
115
+ raise "methods must respond_to? :all?" unless methods.respond_to?(:all?)
90
116
 
91
117
  msg ||= format(DEFAULT_DUCK_FAIL_MSG, "#{obj.class} (#{obj})", methods)
92
118
  match = methods.all? { |method| obj.respond_to?(method) }
@@ -95,7 +121,8 @@ present: %s"
95
121
  obj
96
122
  end
97
123
 
98
- alias assert_type assert_types
99
- alias assert_arr_type assert_arr_types
124
+ alias_method :assert_type, :assert_types
125
+ alias_method :assert_arr_type, :assert_arr_types
126
+ alias_method :assert_common_arr_type, :assert_common_arr_types
100
127
  end
101
128
  end
data/lib/wgit/core_ext.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # Script which extends Ruby's core functionality when parsed.
4
4
  # Needs to be required separately to 'wgit' using `require 'wgit/core_ext'`.
5
5
 
6
- require_relative 'url'
6
+ require_relative "url"
7
7
 
8
8
  # Extend the standard String functionality.
9
9
  class String