scan_left 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f717b22685d20e15af612d1b6beb8da9266b8cf47bf0185e7139c04836d4197a
4
- data.tar.gz: 1f570181040fe2b5ae8b5519af3d2f3d49e8b6a3dc8d3586b8b5420648ca8a74
3
+ metadata.gz: c1557332192560cf4af4dadfe859d6af3daa27fb7a15c4e73095cfbb3edc8e08
4
+ data.tar.gz: e868d3a5e20160daf7aae34d13a53cb28c8e92a63eda2efe0327bcd14e554421
5
5
  SHA512:
6
- metadata.gz: cc9941d161ccfcfd7e3e6346f3e28a2d09ef513eb134efe6ac00dd61e3f9dbc58984f926d16203377fd35aae13b57c423f33657f3e0ff5ab261f2f552c73bda7
7
- data.tar.gz: c1b151cb5caec96b10865a566134664243d611c71b470c6b0bb63f3eecb8e6b227eb16ebd20c9d4227ff34a5fc773bead9ed6ca6484034c4099dc03f44ea44b3
6
+ metadata.gz: 3dd492ba1423dcf94da46b80886e82eee00bf4316a788a8bb56a9650d89d2347a3bc84ebe0b89bd7ba2a4661396879f1804d8453c9ac20646756b3a9589dcc6c
7
+ data.tar.gz: 7fe9dd080e5a6eb203966779f7fab8bc8e52b796bebcadeb9e677564804ee654fe013e25f4c4f919489718e24a9291198aa8328be1157dcd7d68a51f2e9ede71
@@ -0,0 +1,32 @@
1
+ # based on https://github.com/ruby/setup-ruby/blob/master/README.md
2
+ name: Tests
3
+ on: [push, pull_request]
4
+ jobs:
5
+ ci:
6
+ name: CI
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ ubuntu-latest ]
11
+ ruby: [ 2.5, 2.6, 2.7, jruby ]
12
+ runs-on: ${{ matrix.os }}
13
+ env:
14
+ CI: true
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ - uses: actions/cache@v1
21
+ with:
22
+ path: vendor/bundle
23
+ key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
24
+ restore-keys: |
25
+ bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-
26
+ - name: bundle install
27
+ run: |
28
+ ruby -v
29
+ bundle config path vendor/bundle
30
+ bundle install --jobs 4 --retry 3
31
+ - run: bundle exec rubocop
32
+ - run: bundle exec rspec
data/.gitignore CHANGED
@@ -16,22 +16,6 @@
16
16
  # Ignore Byebug command history file.
17
17
  .byebug_history
18
18
 
19
- ## Specific to RubyMotion:
20
- .dat*
21
- .repl_history
22
- build/
23
- *.bridgesupport
24
- build-iPhoneOS/
25
- build-iPhoneSimulator/
26
-
27
- ## Specific to RubyMotion (use of CocoaPods):
28
- #
29
- # We recommend against adding the Pods directory to your .gitignore. However
30
- # you should judge for yourself, the pros and cons are mentioned at:
31
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
- #
33
- # vendor/Pods/
34
-
35
19
  ## Documentation cache and generated files:
36
20
  /.yardoc/
37
21
  /_yardoc/
@@ -45,7 +29,7 @@ build-iPhoneSimulator/
45
29
 
46
30
  # for a library or gem, you might want to ignore these files since the code is
47
31
  # intended to run in multiple environments; otherwise, check them in:
48
- Gemfile.lock
32
+ # Gemfile.lock
49
33
  .ruby-version
50
34
  .ruby-gemset
51
35
  .rspec_status
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ inherit_gem:
2
+ panolint: rubocop.yml
3
+ Metrics/BlockLength:
4
+ Exclude:
5
+ - scan_left.gemspec
6
+ - spec/*.rb
7
+ Style/FrozenStringLiteralComment:
8
+ Exclude:
9
+ - Gemfile
10
+ - Rakefile
11
+ - bin/*
data/CHANGELOG.md CHANGED
@@ -9,12 +9,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Changed
11
11
 
12
- ### Removed
12
+ ### Removed
13
+
14
+ ## [0.1.0] - 2020-05-08
15
+ ### Added
16
+ - Initial implementation of `ScanLeft`
17
+ - Code of conduct
18
+ - Rubocop configuration
19
+ - Tests run via Github Actions configuration
20
+
21
+ ### Changed
22
+ - Bumped bundler version to 2.x
23
+
24
+ ### Removed
25
+ - Scaffolding from the gem skeleton structure
13
26
 
14
27
  ## [0.0.1] - 2020-05-06
15
28
  ### Added
16
29
  - Initial gem project structure
17
30
  - Initial CHANGELOG.md based on keepachangelog.com
18
31
 
19
- [Unreleased]: https://github.com/panorama-ed/scan_left/compare/v0.0.1...HEAD
20
- [0.1.0]: https://github.com/panorama-ed/scan_left/releases/tag/v0.0.1
32
+ [Unreleased]: https://github.com/panorama-ed/scan_left/compare/v0.1.0...HEAD
33
+ [0.1.0]: https://github.com/panorama-ed/scan_left/compare/v0.0.1...v0.1.0
34
+ [0.0.1]: https://github.com/panorama-ed/scan_left/releases/tag/v0.0.1
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at engineering@panoramaed.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in scan_left.gemspec
6
6
  gemspec
7
+
8
+ gem "panolint", github: "panorama-ed/panolint"
data/Gemfile.lock ADDED
@@ -0,0 +1,88 @@
1
+ GIT
2
+ remote: https://github.com/panorama-ed/panolint
3
+ revision: 5d1105a9b762a0d91a2164cafd92853372a126b8
4
+ specs:
5
+ panolint (0.1.2)
6
+ brakeman (~> 4.8)
7
+ rubocop (~> 0.81)
8
+ rubocop-performance (~> 1.5)
9
+ rubocop-rails (~> 2.5)
10
+ rubocop-rspec (~> 1.38)
11
+
12
+ PATH
13
+ remote: .
14
+ specs:
15
+ scan_left (0.1.0)
16
+
17
+ GEM
18
+ remote: https://rubygems.org/
19
+ specs:
20
+ activesupport (6.0.3)
21
+ concurrent-ruby (~> 1.0, >= 1.0.2)
22
+ i18n (>= 0.7, < 2)
23
+ minitest (~> 5.1)
24
+ tzinfo (~> 1.1)
25
+ zeitwerk (~> 2.2, >= 2.2.2)
26
+ ast (2.4.0)
27
+ brakeman (4.8.1)
28
+ concurrent-ruby (1.1.6)
29
+ diff-lcs (1.3)
30
+ i18n (1.8.2)
31
+ concurrent-ruby (~> 1.0)
32
+ jaro_winkler (1.5.4)
33
+ minitest (5.14.0)
34
+ parallel (1.19.1)
35
+ parser (2.7.1.2)
36
+ ast (~> 2.4.0)
37
+ rack (2.2.2)
38
+ rainbow (3.0.0)
39
+ rake (12.3.3)
40
+ rexml (3.2.4)
41
+ rspec (3.9.0)
42
+ rspec-core (~> 3.9.0)
43
+ rspec-expectations (~> 3.9.0)
44
+ rspec-mocks (~> 3.9.0)
45
+ rspec-core (3.9.2)
46
+ rspec-support (~> 3.9.3)
47
+ rspec-expectations (3.9.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.9.0)
50
+ rspec-mocks (3.9.1)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.9.0)
53
+ rspec-support (3.9.3)
54
+ rubocop (0.82.0)
55
+ jaro_winkler (~> 1.5.1)
56
+ parallel (~> 1.10)
57
+ parser (>= 2.7.0.1)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ rexml
60
+ ruby-progressbar (~> 1.7)
61
+ unicode-display_width (>= 1.4.0, < 2.0)
62
+ rubocop-performance (1.5.2)
63
+ rubocop (>= 0.71.0)
64
+ rubocop-rails (2.5.2)
65
+ activesupport
66
+ rack (>= 1.1)
67
+ rubocop (>= 0.72.0)
68
+ rubocop-rspec (1.39.0)
69
+ rubocop (>= 0.68.1)
70
+ ruby-progressbar (1.10.1)
71
+ thread_safe (0.3.6)
72
+ tzinfo (1.2.7)
73
+ thread_safe (~> 0.1)
74
+ unicode-display_width (1.7.0)
75
+ zeitwerk (2.3.0)
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ bundler (~> 2.1)
82
+ panolint!
83
+ rake (~> 12.3)
84
+ rspec (~> 3.0)
85
+ scan_left!
86
+
87
+ BUNDLED WITH
88
+ 2.1.4
data/README.md CHANGED
@@ -1,2 +1,66 @@
1
+ ![Tests](https://github.com/panorama-ed/scan_left/workflows/Tests/badge.svg)
2
+
1
3
  # scan_left
2
- A tiny Ruby gem to provide the 'scan_left' operation on any Ruby Enumerable.
4
+ A tiny Ruby gem to provide the `#scan_left` operation on any Ruby
5
+ `Enumerable`.
6
+
7
+ ## What does it do?
8
+
9
+ Imagine a series of numbers which you want to *sum*. You accomplish
10
+ this by processing all elements, adding each to the previous sum,
11
+ returning the final result.
12
+
13
+ Now imagine that, rather than just the final sum at the end of the
14
+ series, you want *a series* of the partial sums after processing each
15
+ element. This is called a ["Prefix
16
+ Sum"](https://en.wikipedia.org/wiki/Prefix_sum).
17
+
18
+ In functional programming (FP), the *sum* is generalized as the *fold*
19
+ operation, in which an initial state and a binary operation are
20
+ combined to "fold" a series of values into a single result. The
21
+ closely related *prefix sum*, which produces a series of intermediate
22
+ results, is generalized as the *scan* operation. Adding "left" to
23
+ these operation names indicates that calculation is to proceed
24
+ left-to-right.
25
+
26
+ ## Compare / Contrast with #inject
27
+
28
+ Ruby's standard library operation `Enumerable#inject` implements the
29
+ FP fold operation. It also implements the simpler reduce operation,
30
+ which is a fold whose initial state is the first element of the
31
+ series.
32
+
33
+ The key differences between `#inject` and `#scan_left` are:
34
+
35
+ 1. Incremental results: `#scan_left` returns a series of results
36
+ after processing each element of the input series. `#inject`
37
+ returns a single value, which equals the final result in the
38
+ series returned by `#scan_left`.
39
+
40
+ 2. Laziness: `#scan_left` can preserve the laziness of the input
41
+ series. As each incremental result is read from the output
42
+ series, the actual calculation is lazily performed against the
43
+ input. `#inject` cannot be a a lazy operation in general, as its
44
+ single result reflects a calculation across every element of the
45
+ input series.
46
+
47
+ ## Examples
48
+
49
+ ```
50
+ require 'scan_left'
51
+
52
+ ScanLeft.new([]).scan_left(0) { |s, x| s + x } == [0]
53
+ [].inject(0) { |s, x| s + x } == 0
54
+
55
+ ScanLeft.new([1]).scan_left(0) { |s, x| s + x } == [0, 1]
56
+ [1].inject(0) { |s, x| s + x } == 1
57
+
58
+ ScanLeft.new([1, 2, 3]).scan_left(0) { |s, x| s + x } == [0, 1, 3, 6]
59
+ [1, 2, 3].inject(0) { |s, x| s + x } == 6
60
+ ```
61
+
62
+ ## Further Reading
63
+
64
+ * https://en.wikipedia.org/wiki/Fold_(higher-order_function)
65
+ * https://en.wikipedia.org/wiki/Prefix_sum#Scan_higher_order_function
66
+ * http://alvinalexander.com/scala/how-to-walk-scala-collections-reduceleft-foldright-cookbook#scanleft-and-scanright
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/lib/scan_left.rb CHANGED
@@ -1,6 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "scan_left/version"
2
4
 
3
- module ScanLeft
4
- class Error < StandardError; end
5
- # Your code goes here...
5
+ # Original author: [Marc Siegel](https://github.com/ms-ati).
6
+
7
+ # Provides the `#scan_left` operation on any Enumerable.
8
+ class ScanLeft
9
+ attr_reader :enumerable
10
+
11
+ # @param [Enumerable] enumerable Stream to transform via `scan_left`
12
+ def initialize(enumerable)
13
+ @enumerable = enumerable
14
+ end
15
+
16
+ # Map the enumerable to the incremental state of a calculation by
17
+ # applying the given block, in turn, to each element and the
18
+ # previous state of the calculation, resulting in an enumerable of
19
+ # the state after each iteration.
20
+ #
21
+ # @return [Enumerable] Generate a stream of intermediate states
22
+ # resulting from applying a binary operator. Equivalent to a
23
+ # stream of `#inject` calls on first N elements, increasing N from
24
+ # zero to the size of the stream. NOTE: Preserves laziness if
25
+ # `enumerable` is lazy.
26
+ #
27
+ # @param initial [Object] Initial state value to yield.
28
+ #
29
+ # @yield [memo, obj] Invokes given block with previous state value
30
+ # `memo` and next element of the stream `obj`.
31
+ #
32
+ # @yieldreturn [Object] The next state value for `memo`.
33
+ def scan_left(initial)
34
+ memo = initial
35
+ outs = enumerable.map { |*obj| memo = yield(memo, *obj) }
36
+ prepend_preserving_laziness(value: initial, enum: outs)
37
+ end
38
+
39
+ private
40
+
41
+ def prepend_preserving_laziness(value:, enum:)
42
+ case enum
43
+ when Enumerator::Lazy
44
+ [[value], enum].lazy.flat_map { |x| x }
45
+ else
46
+ [value] + enum
47
+ end
48
+ end
6
49
  end
@@ -1,3 +1,5 @@
1
- module ScanLeft
2
- VERSION = "0.0.1"
1
+ # frozen_string_literal: true
2
+
3
+ class ScanLeft
4
+ VERSION = "0.1.0"
3
5
  end
data/scan_left.gemspec CHANGED
@@ -1,39 +1,45 @@
1
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require "scan_left/version"
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = "scan_left"
7
- spec.version = ScanLeft::VERSION
8
- spec.authors = ["Marc Siegel"]
9
- spec.email = ["msiegel@panoramaed.com", "marc@usainnov.com"]
8
+ spec.name = "scan_left"
9
+ spec.version = ScanLeft::VERSION
10
+ spec.authors = ["Marc Siegel"]
11
+ spec.email = ["msiegel@panoramaed.com", "marc@usainnov.com"]
10
12
 
11
- spec.summary = %q{A tiny Ruby gem to provide the 'scan_left' operation on any Ruby Enumerable.}
12
- spec.description = %q{A tiny Ruby gem to provide the 'scan_left' operation on any Ruby Enumerable.}
13
- spec.homepage = "https://github.com/panorama-ed/scan_left"
13
+ spec.summary = "A tiny Ruby gem to provide the 'scan_left' operation on "\
14
+ "any Ruby Enumerable."
15
+ spec.description = spec.summary
16
+ spec.homepage = "https://github.com/panorama-ed/scan_left"
17
+ spec.license = "MIT"
14
18
 
15
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
- # to allow pushing to a single host or delete this section to allow pushing to any host.
17
19
  if spec.respond_to?(:metadata)
18
20
  spec.metadata["allowed_push_host"] = "https://rubygems.org/" # allow pushes
19
21
  spec.metadata["homepage_uri"] = spec.homepage
20
22
  spec.metadata["source_code_uri"] = spec.homepage
21
- spec.metadata["changelog_uri"] = "https://github.com/panorama-ed/scan_left/blob/master/CHANGELOG.md"
23
+ spec.metadata["changelog_uri"] =
24
+ "https://github.com/panorama-ed/scan_left/blob/master/CHANGELOG.md"
22
25
  else
23
- raise "RubyGems 2.0 or newer is required to protect against " \
24
- "public gem pushes."
26
+ raise "RubyGems 2.0 or newer is required to protect against "\
27
+ "public gem pushes."
25
28
  end
26
29
 
27
30
  # Specify which files should be added to the gem when it is released.
28
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
30
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added
32
+ # into git.
33
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
34
+ `git ls-files -z`.
35
+ split("\x0").
36
+ reject { |f| f.match(%r{^(test|spec|features)/}) }
31
37
  end
32
38
  spec.bindir = "exe"
33
39
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
34
40
  spec.require_paths = ["lib"]
35
41
 
36
- spec.add_development_dependency "bundler", "~> 1.17"
42
+ spec.add_development_dependency "bundler", "~> 2.1"
37
43
  spec.add_development_dependency "rake", "~> 12.3"
38
44
  spec.add_development_dependency "rspec", "~> 3.0"
39
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scan_left
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Siegel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-06 00:00:00.000000000 Z
11
+ date: 2020-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.17'
19
+ version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.17'
26
+ version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,11 +60,15 @@ executables: []
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
+ - ".github/workflows/tests.yml"
63
64
  - ".gitignore"
64
65
  - ".rspec"
66
+ - ".rubocop.yml"
65
67
  - ".travis.yml"
66
68
  - CHANGELOG.md
69
+ - CODE_OF_CONDUCT.md
67
70
  - Gemfile
71
+ - Gemfile.lock
68
72
  - LICENSE
69
73
  - README.md
70
74
  - Rakefile
@@ -74,7 +78,8 @@ files:
74
78
  - lib/scan_left/version.rb
75
79
  - scan_left.gemspec
76
80
  homepage: https://github.com/panorama-ed/scan_left
77
- licenses: []
81
+ licenses:
82
+ - MIT
78
83
  metadata:
79
84
  allowed_push_host: https://rubygems.org/
80
85
  homepage_uri: https://github.com/panorama-ed/scan_left