cem 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +57 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cem.gemspec +43 -0
- data/lib/cem.rb +10 -0
- data/lib/cem/ccommon.rb +93 -0
- data/lib/cem/cflame.rb +11 -0
- data/lib/cem/cflame/clickable_img.rb +58 -0
- data/lib/cem/cflame/missing_html.rb +33 -0
- data/lib/cem/cflame/p.rb +37 -0
- data/lib/cem/cflame/progress.rb +48 -0
- data/lib/cem/cruzzles.rb +516 -0
- data/lib/cem/version.rb +3 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7d56d8ba91aab41f5e12070a9f821516b8f69d626d3ffd7261dca402c41768ba
|
4
|
+
data.tar.gz: 513114a63b34c7375339e4eb17d931bc7925bb068495c4726a6577e98c0ebc7a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55106e14c662cec3b4ffc68c6e0bb5775405fadbb61d1fd2d4611becd38d7cf55445e8a7872107797df5aaa4da18656419dbf0ed37eb676dc10a2184aa7168f1
|
7
|
+
data.tar.gz: f8f56d39752badb44074162e20452814937bd40ee44bd0e22ea777bd775f5c830e25768ec21ca104310efa84bb3679e868d99af9a674a907807e479efc43cdf8
|
data/.gitignore
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
.rspec_status
|
8
|
+
/spec/reports/
|
9
|
+
/spec/examples.txt
|
10
|
+
/test/tmp/
|
11
|
+
/test/version_tmp/
|
12
|
+
/tmp/
|
13
|
+
|
14
|
+
# Used by dotenv library to load environment variables.
|
15
|
+
# .env
|
16
|
+
|
17
|
+
# Ignore Byebug command history file.
|
18
|
+
.byebug_history
|
19
|
+
|
20
|
+
## Specific to RubyMotion:
|
21
|
+
.dat*
|
22
|
+
.repl_history
|
23
|
+
build/
|
24
|
+
*.bridgesupport
|
25
|
+
build-iPhoneOS/
|
26
|
+
build-iPhoneSimulator/
|
27
|
+
|
28
|
+
## Specific to RubyMotion (use of CocoaPods):
|
29
|
+
#
|
30
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
31
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
32
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
33
|
+
#
|
34
|
+
# vendor/Pods/
|
35
|
+
|
36
|
+
## Documentation cache and generated files:
|
37
|
+
/.yardoc/
|
38
|
+
/_yardoc/
|
39
|
+
/doc/
|
40
|
+
/rdoc/
|
41
|
+
|
42
|
+
## Environment normalization:
|
43
|
+
/.bundle/
|
44
|
+
/vendor/bundle
|
45
|
+
/lib/bundler/man/
|
46
|
+
|
47
|
+
# for a library or gem, you might want to ignore these files since the code is
|
48
|
+
# intended to run in multiple environments; otherwise, check them in:
|
49
|
+
Gemfile.lock
|
50
|
+
# .ruby-version
|
51
|
+
# .ruby-gemset
|
52
|
+
|
53
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
54
|
+
.rvmrc
|
55
|
+
|
56
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
57
|
+
# .rubocop-https?--*
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 coezbek
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Cem = Christopher's Ruby Gem of Common Helpers
|
2
|
+
|
3
|
+
This gem is an assorted collection of common helpers that I use in a lot of projects.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cem'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install cem
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
require 'cem'
|
24
|
+
|
25
|
+
Key things includes:
|
26
|
+
|
27
|
+
### crequire
|
28
|
+
|
29
|
+
crequire is a replacement for require for situations where bundler would be overkill. It solves the frustration of having to manually install gems to run a particular ruby script which requires other gems.
|
30
|
+
|
31
|
+
For example when you want to require 'qml' from the 'ruby-qml' gem, use the following line:
|
32
|
+
|
33
|
+
```
|
34
|
+
crequire 'qml', 'ruby-qml'
|
35
|
+
```
|
36
|
+
|
37
|
+
This will attempt to require qml and if it can't be found in the local RubyGems, then it will try to `gem install ruby-qml`
|
38
|
+
|
39
|
+
### Array::with_progress
|
40
|
+
|
41
|
+
Monkey patches array to return an enumeration which prints progress while lazy evaluating the enumeration.
|
42
|
+
|
43
|
+
```
|
44
|
+
require 'cem'
|
45
|
+
[a,b,c].with_progress.each { |x|
|
46
|
+
puts x
|
47
|
+
}
|
48
|
+
```
|
49
|
+
|
50
|
+
Uses the [progress bar gem](https://github.com/paul/progress_bar).
|
51
|
+
|
52
|
+
### Numeric monkey patch
|
53
|
+
|
54
|
+
Make all methods in Math module available as methods for numeric (e.g. 3.sqrt instead of Math.sqrt(3))
|
55
|
+
|
56
|
+
### Advent of Code Puzzle Helpers
|
57
|
+
|
58
|
+
Defines integer based `Point2D`, `Seg2D` (line segment), `Dir2D` (relative direction in 2D), `Point3D`, `Grid` (2D array) to help with Advent of Code puzzles, by providing commonly needed methods such as manhattan distances, string to indices conversion, enumeration of adjacent cells, etc.
|
59
|
+
|
60
|
+
## Development
|
61
|
+
|
62
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
63
|
+
|
64
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
65
|
+
|
66
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/coezbek/cem.
|
71
|
+
|
72
|
+
## License
|
73
|
+
|
74
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
75
|
+
|
76
|
+
## What's with the name?
|
77
|
+
|
78
|
+
Cem is pronounced like `Jam` in Turkish, which is close enough to `Gem` and exactely short enough to type.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cem"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/cem.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cem/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cem"
|
8
|
+
spec.version = Cem::VERSION
|
9
|
+
spec.authors = ["Christopher Özbek"]
|
10
|
+
spec.email = ["christopher@oezbek.org"]
|
11
|
+
|
12
|
+
spec.summary = %q{Cem = Christopher's Common Helper Gem for Ruby.}
|
13
|
+
spec.description = %q{Christopher's Gem = My little helpers which I use in many projects.}
|
14
|
+
spec.homepage = "https://github.com/coezbek/cem"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
spec.metadata["source_code_uri"] = "https://github.com/coezbek/cem"
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/coezbek/cem/README.md"
|
24
|
+
else
|
25
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
26
|
+
"public gem pushes."
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
30
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
32
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
end
|
34
|
+
spec.bindir = "exe"
|
35
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
39
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
41
|
+
|
42
|
+
spec.add_runtime_dependency 'flammarion', '~> 0.3'
|
43
|
+
end
|
data/lib/cem.rb
ADDED
data/lib/cem/ccommon.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Require the given ruby file and if loading fails, try to install the gem of the same name or given parameter
|
4
|
+
#
|
5
|
+
def crequire(requireName, gem_name = nil)
|
6
|
+
require requireName
|
7
|
+
rescue LoadError
|
8
|
+
gem_name = requireName if gem_name.nil?
|
9
|
+
system("gem install #{gem_name}")
|
10
|
+
Gem.clear_paths
|
11
|
+
require requireName
|
12
|
+
end
|
13
|
+
|
14
|
+
class File
|
15
|
+
|
16
|
+
# Returns the first match of the given files
|
17
|
+
def self.exist_first?(*candidates)
|
18
|
+
candidates.each { |candidate|
|
19
|
+
if File.exist?(candidate)
|
20
|
+
return candidate
|
21
|
+
end
|
22
|
+
}
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
class Array
|
30
|
+
def with_progress(&block)
|
31
|
+
|
32
|
+
crequire 'progress_bar'
|
33
|
+
|
34
|
+
bar = ProgressBar.new(count)
|
35
|
+
if block
|
36
|
+
each{|obj| yield(obj).tap{bar.increment!}}
|
37
|
+
else
|
38
|
+
Enumerator.new{ |yielder|
|
39
|
+
self.each do |obj|
|
40
|
+
(yielder << obj).tap{bar.increment!}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Returns true if this script is running on the Windows Sub-System for Linux, i.e. under Linux where the host is Windows.
|
49
|
+
#
|
50
|
+
# If true, interoperability with Windows is possible.
|
51
|
+
def wsl?
|
52
|
+
@@wsl ||= File.file?('/proc/version') && File.open('/proc/version', &:gets).downcase.include?("microsoft")
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
#
|
57
|
+
# If on wsl? then the given is converted from a Windows path to a Linux path (e.g. "C:\" becomes "/mnt/c/")
|
58
|
+
#
|
59
|
+
# Uses the wslpath command, passes mode to wslpath:
|
60
|
+
#
|
61
|
+
# -a force result to absolute path format
|
62
|
+
# -u translate from a Windows path to a WSL path (default)
|
63
|
+
# -w translate from a WSL path to a Windows path
|
64
|
+
# -m translate from a WSL path to a Windows path, with '/' instead of '\'
|
65
|
+
#
|
66
|
+
def wslpath(path, mode = "")
|
67
|
+
return path if !wsl?
|
68
|
+
|
69
|
+
return `wslpath #{mode} '#{path}'`.strip
|
70
|
+
end
|
71
|
+
|
72
|
+
def isHTML5Element(element)
|
73
|
+
|
74
|
+
# https://html.spec.whatwg.org/#elements-3
|
75
|
+
|
76
|
+
@valid_elements ||= %w(a abbr address area article aside audio b base bdi bdo blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd label legend li link main map mark math menu meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select slot small source span strong style sub summary sup svg table tbody td template textarea tfoot th thead time title tr track u ul var video wbr)
|
77
|
+
|
78
|
+
return @valid_elements.include?(element.to_s.downcase)
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
module CLog
|
83
|
+
|
84
|
+
def log
|
85
|
+
CLog.log
|
86
|
+
end
|
87
|
+
|
88
|
+
# Global, memoized, lazy initialized instance of a logger
|
89
|
+
def self.log
|
90
|
+
@log ||= Logger.new(STDOUT)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
data/lib/cem/cflame.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Clickable Image module mixin for Flammarion::Writeable
|
3
|
+
#
|
4
|
+
# Features:
|
5
|
+
# - clickable => will call the given block when user clicks image
|
6
|
+
# - update() => can be replaced/updated
|
7
|
+
# - src= => img src can be updated on the fly
|
8
|
+
#
|
9
|
+
module Flammarion::Writeable
|
10
|
+
|
11
|
+
attr_accessor :url, :width, :height, :alt, :title
|
12
|
+
|
13
|
+
class ClickableImage
|
14
|
+
def initialize(url, width, height, alt, title, name, owner, block)
|
15
|
+
@name = name
|
16
|
+
@owner = owner
|
17
|
+
@block = block
|
18
|
+
@url = url
|
19
|
+
@width = width
|
20
|
+
@height = height
|
21
|
+
@alt = alt
|
22
|
+
@title = title
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
return @owner.callback_link(html) {
|
27
|
+
@block.call(self) if @block
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def html
|
32
|
+
%|<img id="#{@name}" width=#{@width.to_s} max-height=#{@height.to_s} src="#{@url.to_s}" alt="#{@alt}" title="#{@title}"/>|
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(url, width, height, alt, title)
|
36
|
+
@url = url
|
37
|
+
@width = width
|
38
|
+
@height = height
|
39
|
+
@alt = alt if alt
|
40
|
+
@title = title if title
|
41
|
+
|
42
|
+
@owner.js("$( '##{@name}' ).replaceWith('#{html}');")
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def url=(value)
|
47
|
+
value = value.to_s
|
48
|
+
# puts "urlset on #{@name}"
|
49
|
+
@owner.js("$( '##{@name}' ).attr('src', '#{value}');")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def img_raw(url, width, height, alt, title, options = {}, &block)
|
55
|
+
return ClickableImage.new(url, width, height, alt, title, @engraving.make_id, self, block)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
# Mixin for Flammarion::Writeable which provides short-cuts for common html elements
|
3
|
+
#
|
4
|
+
# - The method name will become the html element / tag.
|
5
|
+
# - Any un-named parameter will become the value for the element. Several parameters will create several elements of the given tag.
|
6
|
+
# - Any named parameter will become an attribute to all created elements.
|
7
|
+
#
|
8
|
+
# Parameters will not be escaped (raw)!
|
9
|
+
#
|
10
|
+
# Some examples:
|
11
|
+
# f.p => f.puts "<p/>", raw: true
|
12
|
+
# f.h1 "Title" => f.puts "<h1>Title</h2>", raw: true
|
13
|
+
# f.img src: "url", width: 32 => f.puts '<img src="url" width="32"/>', raw: true
|
14
|
+
# f.p "Hello", "World" => f.puts "<p>Hello</p><p>World</p>", raw: true
|
15
|
+
#
|
16
|
+
module Flammarion::Writeable
|
17
|
+
|
18
|
+
def method_missing(m, *args, &block)
|
19
|
+
|
20
|
+
if !isHTML5Element(m)
|
21
|
+
puts "Warning: #{m} is not a valid HTML 5 Tag"
|
22
|
+
end
|
23
|
+
|
24
|
+
attribs = args.last.is_a?(Hash) ? args.pop.map { |key, value| %| #{key.to_s}="#{value}"| }.join : ""
|
25
|
+
|
26
|
+
if args.size == 0
|
27
|
+
puts "<#{m} #{attribs}/>", raw: true
|
28
|
+
else
|
29
|
+
puts args.map { |value| "<#{m} #{attribs}>#{value}</#{m}>" }.join, raw: true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/cem/cflame/p.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Paragraph which can be updated easily as a mixin for Flammarion
|
4
|
+
#
|
5
|
+
module Flammarion::Writeable
|
6
|
+
|
7
|
+
class Paragraph
|
8
|
+
|
9
|
+
def initialize(text, name, owner)
|
10
|
+
@text = text
|
11
|
+
@name = name
|
12
|
+
@owner = owner
|
13
|
+
end
|
14
|
+
|
15
|
+
def html
|
16
|
+
%|<p id="#{@name}">#{@text}</p>|
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
html
|
21
|
+
end
|
22
|
+
|
23
|
+
def text=(newText)
|
24
|
+
return if newText == @text
|
25
|
+
@text = newText
|
26
|
+
|
27
|
+
@owner.js("$( '##{@name}' ).replaceWith('#{html}');")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def p(text)
|
32
|
+
object = Paragraph.new(text, @engraving.make_id, self)
|
33
|
+
puts object, raw:true
|
34
|
+
object
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# A simple CSS-based progress bar for Flammarion.
|
4
|
+
#
|
5
|
+
# Use set() to set the progress as an integer in percent.
|
6
|
+
#
|
7
|
+
# progress = f.progress
|
8
|
+
# ... # do first half
|
9
|
+
# progress.set(50)
|
10
|
+
# ... # finish work
|
11
|
+
# progress.set(100) # => will set to 100%
|
12
|
+
#
|
13
|
+
# Or use each() with an array
|
14
|
+
#
|
15
|
+
# progress.each([1,2,3]) { |x| ... }
|
16
|
+
#
|
17
|
+
module Flammarion::Writeable
|
18
|
+
|
19
|
+
class Progress
|
20
|
+
def initialize(name, owner)
|
21
|
+
@name = name
|
22
|
+
@owner = owner
|
23
|
+
@owner.raw("<div id='progressBar' style='width: 100%; height: 22px; border: 1px solid #8f8f8f; background-color: #292929; border-radius: 2px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25) inset;'><div id='progressBarInner-#{@name}' style='height: 100%; color: #fff; text-align: center; line-height: 22px; width: 0px; background-color: #0099ff;'></div></div>")
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(ary, &block)
|
27
|
+
set(0)
|
28
|
+
@max = ary.size
|
29
|
+
return if @max == 0
|
30
|
+
|
31
|
+
ary.each_with_index { |a, i|
|
32
|
+
yield a
|
33
|
+
set(i * 100 / @max)
|
34
|
+
}
|
35
|
+
ary
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(value)
|
39
|
+
value = value.to_s
|
40
|
+
@owner.js("$( '#progressBarInner-#{@name}' ).width('#{value}%').html('#{value}%');")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def progress(name = nil)
|
46
|
+
return Progress.new(name || @engraving.make_id, self)
|
47
|
+
end
|
48
|
+
end
|
data/lib/cem/cruzzles.rb
ADDED
@@ -0,0 +1,516 @@
|
|
1
|
+
|
2
|
+
##
|
3
|
+
# cruzzles Christopher's Ruby Puzzle Utilities
|
4
|
+
#
|
5
|
+
# Includes:
|
6
|
+
# - Point2D, Grid, Dirs(ections)2D
|
7
|
+
# - Some Numeric hackery
|
8
|
+
#
|
9
|
+
|
10
|
+
def min(a, b)
|
11
|
+
return a <= b ? a : b
|
12
|
+
end
|
13
|
+
|
14
|
+
class Numeric
|
15
|
+
# Make all methods in Math module available as methods for numeric (e.g. 3.sqrt instead of Math.sqrt(3))
|
16
|
+
Math.methods(false).each do |method|
|
17
|
+
define_method method do |*args|
|
18
|
+
Math.send(method, self, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Point2D = Struct.new("Point2D", :x, :y) {
|
24
|
+
def to_s
|
25
|
+
"#{x},#{y}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def flip
|
29
|
+
Point2D.new(-x,-y)
|
30
|
+
end
|
31
|
+
|
32
|
+
def +(other)
|
33
|
+
if other.is_a? Array
|
34
|
+
other.map { |o| self + o }
|
35
|
+
else
|
36
|
+
Point2D.new(x + other.x, y + other.y)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Scalar multiplication
|
41
|
+
def *(other)
|
42
|
+
Point2D.new(x * other, y * other)
|
43
|
+
end
|
44
|
+
|
45
|
+
def manhattan(other)
|
46
|
+
return (x - other.x).abs + (y - other.y).abs
|
47
|
+
end
|
48
|
+
|
49
|
+
# returns the euclidean distance to the given Point2D
|
50
|
+
def dist(other)
|
51
|
+
return ((x - other.x) ** 2 + (y - other.y) ** 2).sqrt
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.from_s(s)
|
55
|
+
|
56
|
+
case s.upcase
|
57
|
+
when 'E', 'RIGHT', 'R', '>'
|
58
|
+
Point2D.new(+1, 0)
|
59
|
+
when 'N', 'UP', 'TOP', 'T', 'U', '^'
|
60
|
+
Point2D.new( 0, -1)
|
61
|
+
when 'S', 'DOWN', 'BOTTOM', 'B', 'D', 'v', 'V'
|
62
|
+
Point2D.new( 0, +1)
|
63
|
+
when 'W', 'LEFT', 'L', '<'
|
64
|
+
Point2D.new(-1, 0)
|
65
|
+
else
|
66
|
+
raise s
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def left
|
72
|
+
return self + Dir2D.LEFT
|
73
|
+
end
|
74
|
+
|
75
|
+
def left!
|
76
|
+
x += Dir2D.LEFT.x
|
77
|
+
y += Dir2D.LEFT.y
|
78
|
+
return self
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def to_dir_bitmask
|
83
|
+
|
84
|
+
if x == 0 && y == -1
|
85
|
+
return 1
|
86
|
+
elsif x == 1 && y == 0
|
87
|
+
return 2
|
88
|
+
elsif x == 0 && y == 1
|
89
|
+
return 4
|
90
|
+
elsif x == -1 && y == 0
|
91
|
+
return 8
|
92
|
+
else
|
93
|
+
raise self.p
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.from_dir_bitmask(v)
|
98
|
+
|
99
|
+
result = []
|
100
|
+
if v & 1 > 0
|
101
|
+
result << Point2D.new(0, -1)
|
102
|
+
end
|
103
|
+
if v & 2 > 0
|
104
|
+
result << Point2D.new(1, 0)
|
105
|
+
end
|
106
|
+
if v & 4 > 0
|
107
|
+
result << Point2D.new(0, 1)
|
108
|
+
end
|
109
|
+
if v & 8 > 0
|
110
|
+
result << Point2D.new(-1, 0)
|
111
|
+
end
|
112
|
+
return result
|
113
|
+
end
|
114
|
+
|
115
|
+
# def <=>(other)
|
116
|
+
# y == other.y ? x <=> other.x : y <=> other.y
|
117
|
+
# end
|
118
|
+
|
119
|
+
def ==(other)
|
120
|
+
y == other.y && x == other.x
|
121
|
+
end
|
122
|
+
}
|
123
|
+
|
124
|
+
Seg2D = Struct.new("Seg2D", :p1, :p2) {
|
125
|
+
def to_s
|
126
|
+
"#{p1},#{p2}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def x_range
|
130
|
+
if p1.x < p2.x
|
131
|
+
return p1.x..p2.x
|
132
|
+
else
|
133
|
+
return p2.x..p1.x
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def y_range
|
138
|
+
if p1.y < p2.y
|
139
|
+
return p1.y..p2.y
|
140
|
+
else
|
141
|
+
return p2.y..p1.y
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
}
|
146
|
+
|
147
|
+
Dirs2D = [Point2D.new(0, -1), Point2D.new(1, 0), Point2D.new(0, 1), Point2D.new(-1, 0)]
|
148
|
+
|
149
|
+
class Dir2D < Point2D
|
150
|
+
|
151
|
+
N = UP = Dir2D.new( 0, -1)
|
152
|
+
E = RIGHT = Dir2D.new( 1, 0)
|
153
|
+
S = DOWN = Dir2D.new( 0, 1)
|
154
|
+
W = LEFT = Dir2D.new(-1, 0)
|
155
|
+
|
156
|
+
#
|
157
|
+
# Dir2D * Range = array of Dir2D
|
158
|
+
# E * (1..3) = [E, E*2, E*3]
|
159
|
+
#
|
160
|
+
def *(other)
|
161
|
+
if other.is_a? Range
|
162
|
+
other.to_a.map { |b| self * b }
|
163
|
+
else
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.vert(length=1)
|
169
|
+
return [N * (1..length), S * (1..length)].flatten
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.hori(length=1)
|
173
|
+
return [E * (1..length), W * (1..length)].flatten
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.x(length=1)
|
177
|
+
return vert(length) + hori(length)
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
class Grid
|
184
|
+
|
185
|
+
attr_reader :data
|
186
|
+
|
187
|
+
def initialize(x, y, default=0, &block)
|
188
|
+
|
189
|
+
@data = []
|
190
|
+
|
191
|
+
if block
|
192
|
+
|
193
|
+
case block.arity
|
194
|
+
when 0
|
195
|
+
y.times {
|
196
|
+
row = []
|
197
|
+
x.times {
|
198
|
+
row << block.call()
|
199
|
+
}
|
200
|
+
@data << row
|
201
|
+
}
|
202
|
+
when 2
|
203
|
+
y.times { |iy|
|
204
|
+
row = []
|
205
|
+
x.times { |ix|
|
206
|
+
row << block.call(iy, ix)
|
207
|
+
}
|
208
|
+
@data << row
|
209
|
+
}
|
210
|
+
else
|
211
|
+
raise
|
212
|
+
end
|
213
|
+
|
214
|
+
else # if no block given
|
215
|
+
|
216
|
+
y.times {
|
217
|
+
row = []
|
218
|
+
x.times {
|
219
|
+
row << default
|
220
|
+
}
|
221
|
+
@data << row
|
222
|
+
}
|
223
|
+
# @data = [[default] * x] * y
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def initialize_copy(orig)
|
228
|
+
super
|
229
|
+
|
230
|
+
@data = Marshal.load(Marshal.dump(orig.data))
|
231
|
+
# Do custom initialization for self
|
232
|
+
end
|
233
|
+
|
234
|
+
def minmax(null=nil)
|
235
|
+
min = nil
|
236
|
+
max = nil
|
237
|
+
|
238
|
+
@data.each_with_index { |l, y|
|
239
|
+
l.each_with_index { |c, x|
|
240
|
+
|
241
|
+
if c != null
|
242
|
+
if min == nil || max == nil
|
243
|
+
min = max = Point2D.new(x,y)
|
244
|
+
end
|
245
|
+
min = Point2D.new([min.x, x].min, [min.y, y].min)
|
246
|
+
max = Point2D.new([max.x, x].max, [min.y, y].max)
|
247
|
+
end
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
251
|
+
return min, max
|
252
|
+
end
|
253
|
+
|
254
|
+
def [](y, x=nil)
|
255
|
+
if x == nil
|
256
|
+
# puts y.inspect
|
257
|
+
if y.respond_to?(:x) && y.respond_to?(:y)
|
258
|
+
return @data[y.y][y.x]
|
259
|
+
end
|
260
|
+
return @data[y]
|
261
|
+
end
|
262
|
+
|
263
|
+
if y.is_a? Range
|
264
|
+
return @data[y].map { |row| row[x] }
|
265
|
+
end
|
266
|
+
return @data[y][x]
|
267
|
+
end
|
268
|
+
|
269
|
+
#
|
270
|
+
# Array assignment to grid.
|
271
|
+
#
|
272
|
+
# grid[y,x] = a # Store a in row y and column x
|
273
|
+
# grid[point2d] = a # Use point2d.y and point2d.x to store a
|
274
|
+
#
|
275
|
+
def []=(y, x, assign=nil)
|
276
|
+
if assign == nil
|
277
|
+
if y.respond_to?(:x) && y.respond_to?(:y)
|
278
|
+
return @data[y.y][y.x] = x
|
279
|
+
end
|
280
|
+
return @data[y] = x
|
281
|
+
end
|
282
|
+
return @data[y][x] = assign
|
283
|
+
end
|
284
|
+
|
285
|
+
def to_a
|
286
|
+
return @data.flatten
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Returns the Point2Ds in the directions NSEW as an array (no diagonals, see adja_index for this)
|
291
|
+
#
|
292
|
+
# Will not return Point2Ds in case we are at the boundary of the Grid
|
293
|
+
#
|
294
|
+
def nsew_index(x,y=nil)
|
295
|
+
|
296
|
+
# puts "#{@data.size} x #{@data[0].size}"
|
297
|
+
|
298
|
+
if y == nil
|
299
|
+
if x.respond_to?(:x) && x.respond_to?(:y)
|
300
|
+
y = x.y
|
301
|
+
x = x.x
|
302
|
+
else
|
303
|
+
raise "Need Point2D with :x and :y or two parameters"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
result = []
|
308
|
+
if x > 0
|
309
|
+
result << Point2D.new(x-1,y)
|
310
|
+
end
|
311
|
+
if y > 0
|
312
|
+
result << Point2D.new(x,y-1)
|
313
|
+
end
|
314
|
+
if y + 1 < @data.size - 1
|
315
|
+
result << Point2D.new(x, y+1)
|
316
|
+
end
|
317
|
+
if x + 1 < @data[0].size - 1
|
318
|
+
result << Point2D.new(x+1,y)
|
319
|
+
end
|
320
|
+
return result
|
321
|
+
end
|
322
|
+
|
323
|
+
#
|
324
|
+
# Will remove all invalid indices from the array
|
325
|
+
#
|
326
|
+
def select_valid(a)
|
327
|
+
a.select { |p|
|
328
|
+
(p.x >= 0) && (p.y >= 0) && (p.y <= @data.size-1) && (p.x <= @data[0].size-1)
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
def adja_index(x,y=nil)
|
333
|
+
if y == nil
|
334
|
+
if x.respond_to?(:x) && x.respond_to?(:y)
|
335
|
+
y = x.y
|
336
|
+
x = x.x
|
337
|
+
else
|
338
|
+
raise "Need Point2D with :x and :y or two parameters"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
result = []
|
343
|
+
if x > 0
|
344
|
+
if y > 0
|
345
|
+
result << Point2D.new(x-1,y-1)
|
346
|
+
end
|
347
|
+
result << Point2D.new(x-1,y)
|
348
|
+
if y + 1 < @data.size - 1
|
349
|
+
result << Point2D.new(x-1, y+1)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
if y > 0
|
353
|
+
result << Point2D.new(x,y-1)
|
354
|
+
end
|
355
|
+
|
356
|
+
# result << Point2D.new(x,y)
|
357
|
+
|
358
|
+
if y + 1 < @data.size - 1
|
359
|
+
result << Point2D.new(x, y+1)
|
360
|
+
end
|
361
|
+
if x + 1 < @data[0].size - 1
|
362
|
+
if y > 0
|
363
|
+
result << Point2D.new(x+1,y-1)
|
364
|
+
end
|
365
|
+
result << Point2D.new(x+1,y)
|
366
|
+
if y + 1 < @data.size - 1
|
367
|
+
result << Point2D.new(x+1, y+1)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
return result
|
371
|
+
end
|
372
|
+
|
373
|
+
def to_s
|
374
|
+
printBoard()
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
#
|
379
|
+
# returns a copy of the underlying 2D array, with linking to the content (rather than dup the content also)
|
380
|
+
#
|
381
|
+
def to_a
|
382
|
+
@data.map { |row| row.dup }
|
383
|
+
end
|
384
|
+
|
385
|
+
def data
|
386
|
+
@data
|
387
|
+
end
|
388
|
+
|
389
|
+
#
|
390
|
+
# Maps each cell of the grid by the given block.
|
391
|
+
#
|
392
|
+
# If block has...
|
393
|
+
# - one parameter, passes the cell value
|
394
|
+
# - two parameters, passes y and x
|
395
|
+
# - three parameteres, passes y, x and the cell value
|
396
|
+
#
|
397
|
+
def map_a(&block)
|
398
|
+
|
399
|
+
case block.arity
|
400
|
+
when 1
|
401
|
+
@data.map { |row| row.map { |value| block.call(value) } }
|
402
|
+
when 2
|
403
|
+
@data.each_with_index.map { |row, y| row.each_index.map { |x| block.call(y,x) } }
|
404
|
+
when 3
|
405
|
+
@data.each_with_index.map { |row, y| row.each_with_index.map { |cell, x| block.call(y,x,cell) } }
|
406
|
+
else
|
407
|
+
raise
|
408
|
+
end
|
409
|
+
|
410
|
+
end
|
411
|
+
|
412
|
+
def each(&block)
|
413
|
+
@data.each { |row|
|
414
|
+
row.each { |cell|
|
415
|
+
block.call(cell)
|
416
|
+
}
|
417
|
+
}
|
418
|
+
end
|
419
|
+
|
420
|
+
def each_with_index(&block)
|
421
|
+
case block.arity
|
422
|
+
when 2
|
423
|
+
@data.each_with_index { |l, y|
|
424
|
+
l.each_with_index { |c, x|
|
425
|
+
block.call(c, Point2D.new(x,y))
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
when 3
|
430
|
+
@data.each_with_index { |l, y|
|
431
|
+
l.each_with_index { |c, x|
|
432
|
+
block.call(c, y, x)
|
433
|
+
}
|
434
|
+
}
|
435
|
+
else
|
436
|
+
raise
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def sum(&block)
|
441
|
+
sum = 0
|
442
|
+
each { |cell|
|
443
|
+
sum += block.call(cell)
|
444
|
+
}
|
445
|
+
return sum
|
446
|
+
end
|
447
|
+
|
448
|
+
def printBoard(overlay = nil)
|
449
|
+
|
450
|
+
result = ""
|
451
|
+
@data.each_with_index { |l, y|
|
452
|
+
l.each_with_index { |c, x|
|
453
|
+
|
454
|
+
pos = Point2D.new(x,y)
|
455
|
+
|
456
|
+
if overlay && overlay.has_key?(pos)
|
457
|
+
result += overlay[pos]
|
458
|
+
else
|
459
|
+
result += c.to_s
|
460
|
+
end
|
461
|
+
}
|
462
|
+
|
463
|
+
result += "\r\n"
|
464
|
+
}
|
465
|
+
return result
|
466
|
+
end
|
467
|
+
|
468
|
+
end
|
469
|
+
|
470
|
+
Point3D = Struct.new("Point3D", :x, :y, :z) {
|
471
|
+
|
472
|
+
def manhattan(other=nil)
|
473
|
+
if other != nil
|
474
|
+
return (self - other).manhattan
|
475
|
+
end
|
476
|
+
return x.abs + y.abs + z.abs
|
477
|
+
end
|
478
|
+
|
479
|
+
def -(other) Point3D.new(x - other.x, y - other.y, z - other.z) end
|
480
|
+
|
481
|
+
def +(other) Point3D.new(x + other.x, y + other.y, z + other.z) end
|
482
|
+
|
483
|
+
def to_s
|
484
|
+
"#{x}, #{y}, #{z}"
|
485
|
+
end
|
486
|
+
|
487
|
+
def []=(i, v)
|
488
|
+
case i
|
489
|
+
when 0
|
490
|
+
self.x = v
|
491
|
+
when 1
|
492
|
+
self.y = v
|
493
|
+
when 2
|
494
|
+
self.z = v
|
495
|
+
else
|
496
|
+
raise
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
def [](i)
|
501
|
+
case i
|
502
|
+
when 0
|
503
|
+
x
|
504
|
+
when 1
|
505
|
+
y
|
506
|
+
when 2
|
507
|
+
z
|
508
|
+
else
|
509
|
+
raise
|
510
|
+
end
|
511
|
+
end
|
512
|
+
}
|
513
|
+
|
514
|
+
|
515
|
+
|
516
|
+
|
data/lib/cem/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christopher Özbek
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: flammarion
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.3'
|
69
|
+
description: Christopher's Gem = My little helpers which I use in many projects.
|
70
|
+
email:
|
71
|
+
- christopher@oezbek.org
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- cem.gemspec
|
86
|
+
- lib/cem.rb
|
87
|
+
- lib/cem/ccommon.rb
|
88
|
+
- lib/cem/cflame.rb
|
89
|
+
- lib/cem/cflame/clickable_img.rb
|
90
|
+
- lib/cem/cflame/missing_html.rb
|
91
|
+
- lib/cem/cflame/p.rb
|
92
|
+
- lib/cem/cflame/progress.rb
|
93
|
+
- lib/cem/cruzzles.rb
|
94
|
+
- lib/cem/version.rb
|
95
|
+
homepage: https://github.com/coezbek/cem
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata:
|
99
|
+
allowed_push_host: https://rubygems.org
|
100
|
+
homepage_uri: https://github.com/coezbek/cem
|
101
|
+
source_code_uri: https://github.com/coezbek/cem
|
102
|
+
changelog_uri: https://github.com/coezbek/cem/README.md
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.7.6
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Cem = Christopher's Common Helper Gem for Ruby.
|
123
|
+
test_files: []
|