workpattern 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -2
- data/README.md +22 -0
- data/lib/workpattern/day.rb +24 -1
- data/lib/workpattern/version.rb +1 -1
- data/lib/workpattern/week.rb +19 -5
- data/lib/workpattern/week_pattern.rb +1 -3
- data/lib/workpattern/workpattern.rb +64 -20
- data/lib/workpattern.rb +20 -0
- data/workpattern.gemspec +18 -9
- metadata +3 -7
- data/Gemfile.lock +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4784e020f63bed8a312b50553d245f54fe9ebf5c003555f706bc6d4b74ca447
|
|
4
|
+
data.tar.gz: f401a880126a8dea9113037a7b20d50ba123ec66727d8e1d35d74bf5d7539f32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a2d348d3aa2a2ecfd1174f1446176b26f7f481af8eafcecdcbf68be8d9594d1e1dac253d93c8d31389a5ba62b23503c80b9ae5b9159df19021fd2bd3f41a9f08
|
|
7
|
+
data.tar.gz: b132fd4596abf3df95b7a955a376e34b7b8796fae5c848014145e91e2565aaeab2c146ce8beb4881852b5d1de989bd98724f352c8cd86a0a68c3fb013c8f7133
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## Workpattern v0.7.0 (unreleased) ##
|
|
2
|
+
|
|
3
|
+
* Added `Workpattern#to_h` — serialises a workpattern to a plain Ruby hash (JSON-safe; pattern bitmaps are hex-encoded strings).
|
|
4
|
+
* Added `Workpattern.from_h(hash, overwrite: false)` — reconstructs a workpattern from a hash produced by `to_h`. Requires symbol keys; when deserialising from JSON use `JSON.parse(json, symbolize_names: true)`.
|
|
5
|
+
* Removed `Workpattern.persistence_class=` and `Workpattern.persistence?` — both were silently non-functional in all prior releases due to a `@@persist`/`@@persistence` naming bug; no working integration exists.
|
|
6
|
+
* Fixed `DEFAULT_NAME` undefined constant in `Workpattern::Workpattern.initialize` (pre-existing; only triggered when calling the inner class constructor directly with no arguments).
|
|
7
|
+
* Fixed `Array.new(LAST_DAY_OF_WEEK)` → `Array.new(LAST_DAY_OF_WEEK + 1)` in `Week.initialize` for consistency (pre-existing; Ruby auto-extends arrays so no runtime difference).
|
|
8
|
+
|
|
1
9
|
## Workpattern v0.6.0 ( 25 Feb, 2021) ##
|
|
2
10
|
|
|
3
11
|
I stopped keeping this Changelog file update back when v0.5.0 was realeased on 19 Oct 2016 and now it is 10Feb 2021 and I'm playing catch-up.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -91,4 +91,26 @@ Workpattern.delete "My Workpattern"
|
|
|
91
91
|
|
|
92
92
|
# Delete all Workpatterns
|
|
93
93
|
Workpattern.clear
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Serialisation
|
|
97
|
+
|
|
98
|
+
A `Workpattern` can be serialised to a plain Ruby hash and restored later. The hash is JSON-safe — pattern bitmaps are stored as hex strings.
|
|
99
|
+
|
|
100
|
+
``` ruby
|
|
101
|
+
# Serialise
|
|
102
|
+
h = mywp.to_h
|
|
103
|
+
|
|
104
|
+
# Persist however you like (file, database, Redis, …)
|
|
105
|
+
json = JSON.generate(h)
|
|
106
|
+
|
|
107
|
+
# Restore — JSON.parse must use symbolize_names: true
|
|
108
|
+
h2 = JSON.parse(json, symbolize_names: true)
|
|
109
|
+
mywp2 = Workpattern.from_h(h2)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If a workpattern with the same name already exists, `from_h` raises `NameError`. Pass `overwrite: true` to replace it:
|
|
113
|
+
|
|
114
|
+
``` ruby
|
|
115
|
+
Workpattern.from_h(h, overwrite: true)
|
|
94
116
|
```
|
data/lib/workpattern/day.rb
CHANGED
|
@@ -2,7 +2,30 @@ module Workpattern
|
|
|
2
2
|
|
|
3
3
|
class Day
|
|
4
4
|
|
|
5
|
-
attr_accessor
|
|
5
|
+
attr_accessor :hours_per_day, :first_working_minute, :last_working_minute
|
|
6
|
+
attr_reader :pattern
|
|
7
|
+
|
|
8
|
+
def pattern=(value)
|
|
9
|
+
@pattern = value
|
|
10
|
+
set_first_and_last_minutes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_h
|
|
14
|
+
{ pattern: @pattern.to_s(16), hours_per_day: @hours_per_day }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_h(h)
|
|
18
|
+
unless h[:hours_per_day].is_a?(Integer) && h[:hours_per_day] > 0 && h[:hours_per_day] <= HOURS_IN_DAY
|
|
19
|
+
raise ArgumentError, "from_h: hours_per_day must be an Integer between 1 and #{HOURS_IN_DAY}"
|
|
20
|
+
end
|
|
21
|
+
unless h[:pattern].is_a?(String) && h[:pattern].length <= 400
|
|
22
|
+
raise ArgumentError, "from_h: pattern must be a hex String of at most 400 characters"
|
|
23
|
+
end
|
|
24
|
+
day = allocate
|
|
25
|
+
day.hours_per_day = h[:hours_per_day]
|
|
26
|
+
day.pattern = h[:pattern].to_i(16)
|
|
27
|
+
day
|
|
28
|
+
end
|
|
6
29
|
|
|
7
30
|
def initialize(hours_per_day = HOURS_IN_DAY, type = WORK_TYPE)
|
|
8
31
|
@hours_per_day = hours_per_day
|
data/lib/workpattern/version.rb
CHANGED
data/lib/workpattern/week.rb
CHANGED
|
@@ -9,18 +9,35 @@ module Workpattern
|
|
|
9
9
|
# @private
|
|
10
10
|
class Week
|
|
11
11
|
attr_accessor :hours_per_day, :start, :finish, :days
|
|
12
|
-
attr_writer :week_total, :total
|
|
13
12
|
|
|
14
13
|
def initialize(start, finish, type = WORK_TYPE, hours_per_day = HOURS_IN_DAY)
|
|
15
14
|
@hours_per_day = hours_per_day
|
|
16
15
|
@start = Time.gm(start.year, start.month, start.day)
|
|
17
16
|
@finish = Time.gm(finish.year, finish.month, finish.day)
|
|
18
|
-
@days = Array.new(LAST_DAY_OF_WEEK)
|
|
17
|
+
@days = Array.new(LAST_DAY_OF_WEEK + 1)
|
|
19
18
|
FIRST_DAY_OF_WEEK.upto(LAST_DAY_OF_WEEK) do |i|
|
|
20
19
|
@days[i] = Day.new(hours_per_day, type)
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
|
|
23
|
+
def to_h
|
|
24
|
+
{ start: { year: @start.year, month: @start.month, day: @start.day },
|
|
25
|
+
finish: { year: @finish.year, month: @finish.month, day: @finish.day },
|
|
26
|
+
days: (FIRST_DAY_OF_WEEK..LAST_DAY_OF_WEEK).map { |i| @days[i].to_h } }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.from_h(h)
|
|
30
|
+
s = h[:start]
|
|
31
|
+
f = h[:finish]
|
|
32
|
+
week = allocate
|
|
33
|
+
week.hours_per_day = HOURS_IN_DAY
|
|
34
|
+
week.start = Time.gm(s[:year], s[:month], s[:day])
|
|
35
|
+
week.finish = Time.gm(f[:year], f[:month], f[:day])
|
|
36
|
+
week.days = Array.new(LAST_DAY_OF_WEEK + 1)
|
|
37
|
+
h[:days].each_with_index { |dh, i| week.days[i] = Day.from_h(dh) }
|
|
38
|
+
week
|
|
39
|
+
end
|
|
40
|
+
|
|
24
41
|
def <=>(other)
|
|
25
42
|
return -1 if start < other.start
|
|
26
43
|
return 0 if start == other.start
|
|
@@ -270,9 +287,6 @@ module Workpattern
|
|
|
270
287
|
time + DAY
|
|
271
288
|
end
|
|
272
289
|
|
|
273
|
-
def prev_day(time)
|
|
274
|
-
time - DAY
|
|
275
|
-
end
|
|
276
290
|
|
|
277
291
|
def jd(time)
|
|
278
292
|
Time.gm(time.year, time.month, time.day)
|
|
@@ -40,11 +40,9 @@ module Workpattern
|
|
|
40
40
|
# @see #working
|
|
41
41
|
# @see #resting
|
|
42
42
|
#
|
|
43
|
-
def workpattern(opts = {}
|
|
43
|
+
def workpattern(opts = {})
|
|
44
44
|
args = all_workpattern_options(opts)
|
|
45
45
|
|
|
46
|
-
persist.store(name: @name, workpattern: args) if !persist.nil?
|
|
47
|
-
|
|
48
46
|
args = standardise_args(args)
|
|
49
47
|
|
|
50
48
|
upd_start = work_pattern.to_utc(args[:start])
|
|
@@ -19,10 +19,6 @@ module Workpattern
|
|
|
19
19
|
@@workpatterns
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def workpatterns
|
|
23
|
-
@@workpatterns
|
|
24
|
-
end
|
|
25
|
-
|
|
26
22
|
# @!attribute [r] name
|
|
27
23
|
# Name given to the <tt>Workpattern</tt>
|
|
28
24
|
# @!attribute [r] base
|
|
@@ -38,16 +34,6 @@ module Workpattern
|
|
|
38
34
|
#
|
|
39
35
|
attr_reader :name, :base, :span, :from, :to, :weeks
|
|
40
36
|
|
|
41
|
-
# Class for handling persistence in user's own way
|
|
42
|
-
#
|
|
43
|
-
def self.persistence_class=(klass)
|
|
44
|
-
@@persist = klass
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def self.persistence?
|
|
48
|
-
@@persist ||= nil
|
|
49
|
-
end
|
|
50
|
-
|
|
51
37
|
# Holds local timezone info
|
|
52
38
|
@@tz = nil
|
|
53
39
|
|
|
@@ -75,7 +61,7 @@ module Workpattern
|
|
|
75
61
|
# 31st December.
|
|
76
62
|
# @raise [NameError] if the given name already exists
|
|
77
63
|
#
|
|
78
|
-
def initialize(name =
|
|
64
|
+
def initialize(name = DEFAULT_WORKPATTERN_NAME, base = DEFAULT_BASE_YEAR, span = DEFAULT_SPAN)
|
|
79
65
|
if workpatterns.key?(name)
|
|
80
66
|
raise(NameError, "Workpattern '#{name}' already exists and can't be created again")
|
|
81
67
|
end
|
|
@@ -97,6 +83,11 @@ module Workpattern
|
|
|
97
83
|
@week_pattern
|
|
98
84
|
end
|
|
99
85
|
|
|
86
|
+
private def workpatterns
|
|
87
|
+
@@workpatterns
|
|
88
|
+
end
|
|
89
|
+
public
|
|
90
|
+
|
|
100
91
|
# Deletes all <tt>Workpattern</tt> objects
|
|
101
92
|
#
|
|
102
93
|
def self.clear
|
|
@@ -151,11 +142,7 @@ module Workpattern
|
|
|
151
142
|
# @see #resting
|
|
152
143
|
#
|
|
153
144
|
def workpattern(opts = {})
|
|
154
|
-
|
|
155
|
-
week_pattern.workpattern(opts, @@persistence)
|
|
156
|
-
else
|
|
157
|
-
week_pattern.workpattern(opts)
|
|
158
|
-
end
|
|
145
|
+
week_pattern.workpattern(opts)
|
|
159
146
|
end
|
|
160
147
|
|
|
161
148
|
# Convenience method that calls <tt>#workpattern</tt> with the
|
|
@@ -178,6 +165,63 @@ module Workpattern
|
|
|
178
165
|
workpattern(args)
|
|
179
166
|
end
|
|
180
167
|
|
|
168
|
+
def to_h
|
|
169
|
+
{ version: 1,
|
|
170
|
+
name: @name,
|
|
171
|
+
base: @base,
|
|
172
|
+
span: @span,
|
|
173
|
+
weeks: @weeks.map(&:to_h) }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def self.from_h(hash, overwrite: false)
|
|
177
|
+
unless hash.key?(:version)
|
|
178
|
+
raise ArgumentError, "from_h: hash is missing a :version key " \
|
|
179
|
+
"(if deserialising from JSON, use symbolize_names: true)"
|
|
180
|
+
end
|
|
181
|
+
unless hash[:version] == 1
|
|
182
|
+
raise ArgumentError, "from_h: unsupported version #{hash[:version].inspect} " \
|
|
183
|
+
"(supported: 1)"
|
|
184
|
+
end
|
|
185
|
+
unless hash[:name].is_a?(String) && !hash[:name].empty?
|
|
186
|
+
raise ArgumentError, "from_h: :name must be a non-empty String"
|
|
187
|
+
end
|
|
188
|
+
unless hash[:base].is_a?(Integer)
|
|
189
|
+
raise ArgumentError, "from_h: :base must be an Integer"
|
|
190
|
+
end
|
|
191
|
+
unless hash[:span].is_a?(Integer) && hash[:span] != 0
|
|
192
|
+
raise ArgumentError, "from_h: :span must be a non-zero Integer"
|
|
193
|
+
end
|
|
194
|
+
unless hash[:weeks].is_a?(Array)
|
|
195
|
+
raise ArgumentError, "from_h: :weeks must be an Array"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
name = hash[:name]
|
|
199
|
+
if workpatterns.key?(name) && !overwrite
|
|
200
|
+
raise NameError, "Workpattern '#{name}' already exists and can't be created again"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
wp = allocate
|
|
204
|
+
wp.instance_variable_set(:@name, name)
|
|
205
|
+
wp.instance_variable_set(:@base, hash[:base])
|
|
206
|
+
wp.instance_variable_set(:@span, hash[:span])
|
|
207
|
+
|
|
208
|
+
offset = hash[:span] < 0 ? hash[:span].abs - 1 : 0
|
|
209
|
+
from_time = Time.gm(hash[:base].abs - offset)
|
|
210
|
+
to_time = Time.gm(from_time.year + hash[:span].abs - 1, 12, 31, 23, 59)
|
|
211
|
+
wp.instance_variable_set(:@from, from_time)
|
|
212
|
+
wp.instance_variable_set(:@to, to_time)
|
|
213
|
+
|
|
214
|
+
weeks = SortedSet.new
|
|
215
|
+
hash[:weeks].each { |wh| weeks << Week.from_h(wh) }
|
|
216
|
+
raise ArgumentError, "from_h: :weeks must not be empty" if weeks.empty?
|
|
217
|
+
wp.instance_variable_set(:@weeks, weeks)
|
|
218
|
+
wp.instance_variable_set(:@week_pattern, WeekPattern.new(wp))
|
|
219
|
+
|
|
220
|
+
workpatterns.delete(name) if overwrite
|
|
221
|
+
workpatterns[name] = wp
|
|
222
|
+
wp
|
|
223
|
+
end
|
|
224
|
+
|
|
181
225
|
# Calculates the resulting date when the <tt>duration</tt> in minutes
|
|
182
226
|
# is added to the <tt>start</tt> date.
|
|
183
227
|
# The <tt>duration</tt> is always in whole minutes and subtracts from
|
data/lib/workpattern.rb
CHANGED
|
@@ -75,6 +75,26 @@ module Workpattern
|
|
|
75
75
|
Workpattern.clear
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
# Convenience method to deserialise a Workpattern from a plain hash.
|
|
79
|
+
#
|
|
80
|
+
# @param [Hash] hash produced by Workpattern#to_h
|
|
81
|
+
# @param [Boolean] overwrite replace an existing same-named workpattern
|
|
82
|
+
# @return [Workpattern]
|
|
83
|
+
# @raise [ArgumentError] if the hash is missing or has an unsupported version
|
|
84
|
+
# @raise [NameError] if a same-named workpattern already exists and overwrite is false
|
|
85
|
+
#
|
|
86
|
+
def self.from_h(hash, overwrite: false)
|
|
87
|
+
Workpattern.from_h(hash, overwrite: overwrite)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Convenience method to access the registry of all known Workpattern objects.
|
|
91
|
+
#
|
|
92
|
+
# @return [Hash]
|
|
93
|
+
#
|
|
94
|
+
def self.workpatterns
|
|
95
|
+
Workpattern.workpatterns
|
|
96
|
+
end
|
|
97
|
+
|
|
78
98
|
# Convenience method to create a Clock object. This can be used for
|
|
79
99
|
# specifying times if you don't want to create a <tt>DateTime</tt> object
|
|
80
100
|
#
|
data/workpattern.gemspec
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
if RUBY_VERSION >= "2.4"
|
|
5
|
+
require_relative "lib/workpattern/version"
|
|
6
|
+
else
|
|
7
|
+
require "workpattern/version"
|
|
8
|
+
end
|
|
5
9
|
|
|
6
10
|
Gem::Specification.new do |spec|
|
|
7
11
|
spec.name = "workpattern"
|
|
@@ -14,16 +18,20 @@ Gem::Specification.new do |spec|
|
|
|
14
18
|
spec.homepage = "http://workpattern.org"
|
|
15
19
|
spec.license = "MIT"
|
|
16
20
|
spec.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
if RUBY_VERSION >= "2.4"
|
|
22
|
+
spec.metadata["homepage_url"] = spec.homepage
|
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/callenb/workpattern"
|
|
24
|
+
spec.metadata["changelog_uri"] = "https://workpattern.org/2021/02/25/changelog.html"
|
|
25
|
+
end
|
|
22
26
|
# Specify which files should be added to the gem when it is released.
|
|
23
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if RUBY_VERSION >= "2.4"
|
|
29
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
30
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
spec.files = `git ls-files`.split("\n")
|
|
34
|
+
end
|
|
27
35
|
spec.require_paths = ["lib"]
|
|
28
36
|
|
|
29
37
|
# Uncomment to register a new dependency of your gem
|
|
@@ -33,6 +41,7 @@ Gem::Specification.new do |spec|
|
|
|
33
41
|
# guide at: https://bundler.io/guides/creating_gem.html
|
|
34
42
|
spec.add_runtime_dependency 'tzinfo'
|
|
35
43
|
spec.add_runtime_dependency 'sorted_set' if RUBY_VERSION >= "2.4"
|
|
44
|
+
|
|
36
45
|
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
37
46
|
spec.require_paths = ["lib"]
|
|
38
47
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: workpattern
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Barrie Callender
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: tzinfo
|
|
@@ -51,7 +50,6 @@ files:
|
|
|
51
50
|
- CHANGELOG.md
|
|
52
51
|
- CODE_OF_CONDUCT.md
|
|
53
52
|
- Gemfile
|
|
54
|
-
- Gemfile.lock
|
|
55
53
|
- LICENSE.txt
|
|
56
54
|
- README.md
|
|
57
55
|
- Rakefile
|
|
@@ -75,7 +73,6 @@ metadata:
|
|
|
75
73
|
homepage_url: http://workpattern.org
|
|
76
74
|
source_code_uri: https://github.com/callenb/workpattern
|
|
77
75
|
changelog_uri: https://workpattern.org/2021/02/25/changelog.html
|
|
78
|
-
post_install_message:
|
|
79
76
|
rdoc_options: []
|
|
80
77
|
require_paths:
|
|
81
78
|
- lib
|
|
@@ -90,8 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
90
87
|
- !ruby/object:Gem::Version
|
|
91
88
|
version: '0'
|
|
92
89
|
requirements: []
|
|
93
|
-
rubygems_version: 3.
|
|
94
|
-
signing_key:
|
|
90
|
+
rubygems_version: 3.6.7
|
|
95
91
|
specification_version: 4
|
|
96
92
|
summary: Calculates dates and durations whilst taking into account working and non-working
|
|
97
93
|
periods down to a minute
|
data/Gemfile.lock
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
workpattern (0.6.0)
|
|
5
|
-
sorted_set
|
|
6
|
-
tzinfo
|
|
7
|
-
|
|
8
|
-
GEM
|
|
9
|
-
remote: http://rubygems.org/
|
|
10
|
-
specs:
|
|
11
|
-
concurrent-ruby (1.1.8)
|
|
12
|
-
minitest (5.14.4)
|
|
13
|
-
rake (13.0.3)
|
|
14
|
-
rbtree (0.4.4)
|
|
15
|
-
set (1.0.1)
|
|
16
|
-
sorted_set (1.0.3)
|
|
17
|
-
rbtree
|
|
18
|
-
set (~> 1.0)
|
|
19
|
-
tzinfo (2.0.4)
|
|
20
|
-
concurrent-ruby (~> 1.0)
|
|
21
|
-
|
|
22
|
-
PLATFORMS
|
|
23
|
-
x86_64-linux
|
|
24
|
-
|
|
25
|
-
DEPENDENCIES
|
|
26
|
-
minitest (~> 5.0)
|
|
27
|
-
rake (~> 13.0)
|
|
28
|
-
workpattern!
|
|
29
|
-
|
|
30
|
-
BUNDLED WITH
|
|
31
|
-
2.2.9
|