dicey 0.15.0 → 0.15.1

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: a269ac8baf4bf849781175b605d92c23ff95fae3e53baa540519ece9947cec0d
4
- data.tar.gz: af8f74b92b35a2818d18dc91d9917d8af6e91e16f8674f02eebfb5e56b1759fd
3
+ metadata.gz: 315d35b504558fad6ce08ebd3e648903e9f85e97dd5388c20afbcff896c40825
4
+ data.tar.gz: f4138742272906c291d44533391e3b63216c45f8bd99337f8c570e252bd0ce3b
5
5
  SHA512:
6
- metadata.gz: 1b031d4ee67b9c3b1ebe0ba23ac2289f7d14536af4fda1b98b7f481fd9e027ef3b57d8911f7d3c78ca46da0554b63226312e7ae4fa136fc34431bf7f86b7f349
7
- data.tar.gz: ac52115c59f846e4662ed055e871bf31f2e87b2080acb07042730698d5fa6d107daaf64792645752b47bced701ace913b58464a7a96e56dffe6d3aba3a0af2d8
6
+ metadata.gz: 201115ad574e086ab99bf62d0129ae2dd3457cf2c06639d2e18f95c16699fa42f0ec72852aa13bdea408001a27fc366c1150d7cef2874f227957f5cd0e80bc40
7
+ data.tar.gz: abfec7efee1b05e607432650a6e5de444e2cf75d9c462e9b6a0211cb48888234ccf4e95ddb50d431017ddc01d5969d6ca4b6a130faefd31b7f852dc3c41a9200
data/README.md CHANGED
@@ -28,7 +28,8 @@ In seriousness, this program is mainly useful for calculating total frequency (p
28
28
  - [Usage: API](#usage-api)
29
29
  - [Dice](#dice)
30
30
  - [Rolling](#rolling)
31
- - [Calculators](#calculators)
31
+ - [Distribution calculators](#distribution-calculators)
32
+ - [Distribution properties](#distribution-properties)
32
33
  - [Diving deeper](#diving-deeper)
33
34
  - [Development](#development)
34
35
  - [Contributing](#contributing)
@@ -75,7 +76,6 @@ gem "dicey", "~> 0.14"
75
76
 
76
77
  **Dicey** is tested to work on CRuby 3.0+, latest JRuby and TruffleRuby. Compatible implementations should work too.
77
78
  - JSON and YAML formatting require `json` and `yaml`.
78
- - Decimal dice require `bigdecimal`.
79
79
 
80
80
  Otherwise, there are no direct dependencies.
81
81
 
@@ -111,13 +111,13 @@ If probability is preferred, there is an option for that:
111
111
  ```sh
112
112
  $ dicey 4 4 --result probabilities # or -r p for short
113
113
  # D4+D4
114
- 2 => 0.0625
115
- 3 => 0.125
116
- 4 => 0.1875
117
- 5 => 0.25
118
- 6 => 0.1875
119
- 7 => 0.125
120
- 8 => 0.0625
114
+ 2 => 1/16
115
+ 3 => 1/8
116
+ 4 => 3/16
117
+ 5 => 1/4
118
+ 6 => 3/16
119
+ 7 => 1/8
120
+ 8 => 1/16
121
121
  ```
122
122
 
123
123
  This shows that 5 will probably be rolled a quarter of the time.
@@ -230,9 +230,9 @@ You have a sudden urge to roll dice while only having boring integer dice at hom
230
230
 
231
231
  Look no further than **roll** mode introduced in **Dicey** 0.12:
232
232
  ```sh
233
- $ dicey 0.5,1.5,2.5 4 --mode roll # As always, can be abbreviated to -m r
234
- # (0.5e0,0.15e1,0.25e1)+D4
235
- roll => 0.35e1 # You probably will get a different value here.
233
+ $ dicey 0.5,1.0,1.5,2.0,2.5 4 --mode roll # As always, can be abbreviated to -m r
234
+ # (1/2,1,3/2,2,5/2)+D4
235
+ roll => 7/2 # You probably will get a different value here.
236
236
  ```
237
237
 
238
238
  > [!NOTE]
@@ -333,10 +333,10 @@ die.roll
333
333
  > [!NOTE]
334
334
  > 💡 Randomness source is *global*, shared between all dice and probably not thread-safe.
335
335
 
336
- ### Calculators
336
+ ### Distribution calculators
337
337
 
338
- Frequency calculators live in `Dicey::SumFrequencyCalculators` module. There are four calculators currently:
339
- - `Dicey::SumFrequencyCalculators::KroneckerSubstitution` is the recommended calculator, able to handle all `Dicey::RegularDie`. It is very fast, calculating distribution for *100d6* in about 0.1 seconds on my laptop.
338
+ Distribution calculators live in `Dicey::SumFrequencyCalculators` module. There are four calculators currently:
339
+ - `Dicey::SumFrequencyCalculators::KroneckerSubstitution` is the recommended calculator, able to handle all `Dicey::RegularDie`. It is very fast, calculating distribution for *100d6* in about 0.1 seconds on a laptop.
340
340
  - `Dicey::SumFrequencyCalculators::MultinomialCoefficients` is specialized for repeated numeric dice, with performance only slightly worse. However, it is currently limited to dice with arithmetic sequences.
341
341
  - `Dicey::SumFrequencyCalculators::BruteForce` is the most generic and slowest one, but can in principle work with any dice. Currently, it is also limited to `Dicey::NumericDie`, as it's unclear how to handle other values.
342
342
  - `Dicey::SumFrequencyCalculators::Empirical`. This is more of a tool than a calculator. It can be interesting to play around with and see how practical results compare to theoretical ones.
@@ -345,7 +345,57 @@ Calculators inherit from `Dicey::SumFrequencyCalculators::BaseCalculator` and pr
345
345
  - `#call(dice, result_type: {:frequencies | :probabilities}, **options) : Hash`
346
346
  - `#valid_for?(dice) : Boolean`
347
347
 
348
- See [next section](#diving-deeper) for more details on limitations and complexity considerations.
348
+ See [Diving deeper](#diving-deeper) for more details on limitations and complexity considerations.
349
+
350
+ ### Distribution properties
351
+
352
+ While distribution itself is already enough in most cases (we are talking just dice here, after all). it may be of interest to calculate properties of it: mode, mean, expected value, standard deviation, etc. `Dicey::DistributionPropertiesCalculator` already provides this functionality:
353
+ ```rb
354
+ Dicey::DistributionPropertiesCalculator.new.call(
355
+ Dicey::SumFrequencyCalculators::KroneckerSubstitution.new.call(
356
+ Dicey::RegularDie.from_count(2, 3)
357
+ )
358
+ )
359
+ # =>
360
+ # {:mode=>[4],
361
+ # :min=>2,
362
+ # :max=>6,
363
+ # :total_range=>4,
364
+ # :mid_range=>4,
365
+ # :median=>4,
366
+ # :arithmetic_mean=>4,
367
+ # :expected_value=>4,
368
+ # :variance=>(4/3),
369
+ # :standard_deviation=>1.1547005383792515,
370
+ # :skewness=>0.0,
371
+ # :kurtosis=>(9/4),
372
+ # :excess_kurtosis=>(-3/4)}
373
+ ```
374
+
375
+ Of course, for regular dice most properties are quite simple and predicatable due to symmetricity of distribution. It becomes more interesting with unfair, lopsided dice. Remember [Example 3](#example-3-custom-dice)?
376
+ ```rb
377
+ Dicey::DistributionPropertiesCalculator.new.call(
378
+ Dicey::SumFrequencyCalculators::KroneckerSubstitution.new.call(
379
+ [Dicey::RegularDie.new(4), Dicey::NumericDie.new([1,3,4])]
380
+ )
381
+ )
382
+ # =>
383
+ # {:mode=>[5],
384
+ # :min=>2,
385
+ # :max=>8,
386
+ # :total_range=>6,
387
+ # :mid_range=>5,
388
+ # :median=>5,
389
+ # :arithmetic_mean=>5,
390
+ # :expected_value=>(31/6),
391
+ # :variance=>(101/36),
392
+ # :standard_deviation=>1.674979270186815,
393
+ # :skewness=>-0.15762965389465178,
394
+ # :kurtosis=>(23145/10201),
395
+ # :excess_kurtosis=>(-7458/10201)}
396
+ ```
397
+
398
+ This disitrubution is obviosuly skewed (as can be immediately seen from non-zero skewness), with expected value no longer equal to mean. This is a mild example. It is easily possible to create a distribution with multiple local maxima and high skewness.
349
399
 
350
400
  ## Diving deeper
351
401
 
@@ -416,7 +466,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/trinis
416
466
  - Tests cover the behavior and its interactions. 100% coverage *is not enough*, as it does not guarantee that all code paths are tested.
417
467
  - Documentation is up-to-date: generate it with `rake docs` and read it.
418
468
  - "*CHANGELOG.md*" lists the change if it has impact on users.
419
- - "*README.md*" is updated if the feature should be visible there, including the Kanban board.
469
+ - "*README.md*" is updated if the feature should be visible there.
420
470
 
421
471
  ## License
422
472
 
@@ -3,9 +3,13 @@
3
3
  require_relative "numeric_die"
4
4
  require_relative "regular_die"
5
5
 
6
+ require_relative "rational_to_integer"
7
+
6
8
  module Dicey
7
9
  # Helper class to define die definitions and automatically select the best one.
8
10
  class DieFoundry
11
+ include RationalToInteger
12
+
9
13
  # Regexp for matching a possible count.
10
14
  PREFIX = /(?>(?<count>[1-9]\d*+)?d)?+/i
11
15
 
@@ -17,7 +21,7 @@ module Dicey
17
21
  [/\A#{PREFIX}\(?(?<begin>-?\d++)(?>[-–—…]|\.{2,3})(?<end>-?\d++)\)?\z/, :range_mold].freeze,
18
22
  # List of numbers goes into the NumericDie mold.
19
23
  [/\A#{PREFIX}\(?(?<sides>-?\d++(?>,(?>-?\d++)?)+|,)\)?\z/, :weirdly_shaped_mold].freeze,
20
- # Non-integers require arbitrary precision arithmetic, which is not enabled by default.
24
+ # Non-integers require special handling for precision.
21
25
  [/\A#{PREFIX}\(?(?<sides>-?\d++(?>\.\d++)?(?>,(?>-?\d++(?>\.\d++)?)?)+|,)\)?\z/,
22
26
  :weirdly_precise_mold].freeze,
23
27
  # Anything else is spilled on the floor.
@@ -30,7 +34,7 @@ module Dicey
30
34
  # - integer range (like "3-6" or "(-5..5)"), which produces a {NumericDie};
31
35
  # - list of integers (like "3,4,5", "(-1,0,1)", or "2,"), which produces a {NumericDie};
32
36
  # - list of decimal numbers (like "0.5,0.2,0.8" or "(2.0,)"), which produces a {NumericDie},
33
- # but uses +BigDecimal+ for values to maintain precise results.
37
+ # but uses +Rational+ for values to maintain precise results.
34
38
  #
35
39
  # Any die definition can be prefixed with a count, like "2D6" or "1d1,3,5" to create an array.
36
40
  # A plain "d" without an explicit count is ignored instead, creating a single die.
@@ -69,9 +73,7 @@ module Dicey
69
73
  end
70
74
 
71
75
  def weirdly_precise_mold(definition)
72
- require "bigdecimal" unless defined?(BigDecimal)
73
-
74
- sides = definition[:sides].split(",").map { BigDecimal(_1) }
76
+ sides = definition[:sides].split(",").map { rational_to_integer(Rational(_1)) }
75
77
  build_dice(NumericDie, definition[:count], sides)
76
78
  end
77
79
 
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rational_to_integer"
4
+
5
+ module Dicey
6
+ # Calculates distribution properties,
7
+ # also known as descriptive statistics when applied to a population sample.
8
+ #
9
+ # These are well-known properties such as:
10
+ # - min, max, mid-range;
11
+ # - mode, median, arithmetic mean;
12
+ # - important moments (expected value, variance, skewness, kurtosis).
13
+ #
14
+ # It is notable that most dice create symmetric distributions,
15
+ # which means that skewness is 0, while properties denoting center in some way
16
+ # (median, mean, ...) are all equal.
17
+ # Mode is often not unique, but includes this center.
18
+ class DistributionPropertiesCalculator
19
+ include RationalToInteger
20
+
21
+ # Calculate properties for a given distribution.
22
+ #
23
+ # Depending on values in the distribution, some properties may be undefined.
24
+ # In such cases, only mode is guaranteed to be present.
25
+ #
26
+ # @param distribution [Hash{Numeric => Numeric}
27
+ # numeric distribution with pre-sorted keys
28
+ # @return [Hash{Symbol => Numeric, Array<Numeric>}]
29
+ def call(distribution)
30
+ return {} if distribution.empty?
31
+
32
+ calculate_properties(distribution)
33
+ end
34
+
35
+ private
36
+
37
+ def calculate_properties(distribution)
38
+ outcomes = distribution.keys
39
+ weights = distribution.values
40
+
41
+ {
42
+ mode: mode(outcomes, weights),
43
+ **range_characteristics(outcomes),
44
+ **median(outcomes),
45
+ **means(outcomes, weights),
46
+ **moments(distribution),
47
+ }
48
+ end
49
+
50
+ def mode(outcomes, weights)
51
+ max_weight = weights.max
52
+ outcomes.select.with_index { |_, index| weights[index] == max_weight }
53
+ end
54
+
55
+ def range_characteristics(outcomes)
56
+ min = outcomes.min
57
+ max = outcomes.max
58
+ {
59
+ min: min,
60
+ max: max,
61
+ total_range: max - min,
62
+ mid_range: rational_to_integer(Rational(min + max, 2)),
63
+ }
64
+ rescue ArgumentError, TypeError, NoMethodError
65
+ # Outcomes are not comparable with each other, so a range can not be determined.
66
+ {}
67
+ end
68
+
69
+ def means(outcomes, _weights)
70
+ {
71
+ arithmetic_mean: rational_to_integer(Rational(outcomes.sum, outcomes.size)),
72
+ }
73
+ rescue ArgumentError, TypeError
74
+ # Outcomes are not summable with each other, means are meaningless.
75
+ {}
76
+ end
77
+
78
+ def median(outcomes)
79
+ outcomes = outcomes.sort
80
+ value =
81
+ if outcomes.size.odd?
82
+ outcomes[outcomes.size / 2]
83
+ else
84
+ Rational(outcomes[(outcomes.size / 2) - 1] + outcomes[outcomes.size / 2], 2)
85
+ end
86
+ { median: value }
87
+ rescue ArgumentError, TypeError, NoMethodError
88
+ # Outcomes are not compatible with each other, so a median can not be determined.
89
+ {}
90
+ end
91
+
92
+ def moments(distribution)
93
+ total_weight = distribution.values.sum
94
+ expected_value = rational_to_integer(moment(distribution, total_weight, 1))
95
+ variance = rational_to_integer(moment(distribution, total_weight, 2) - (expected_value**2))
96
+ skewness =
97
+ rational_to_integer(moment(distribution, total_weight, 3, expected_value, variance))
98
+ kurtosis =
99
+ rational_to_integer(moment(distribution, total_weight, 4, expected_value, variance))
100
+
101
+ {
102
+ expected_value: expected_value,
103
+ variance: variance,
104
+ standard_deviation: Math.sqrt(variance),
105
+ skewness: skewness,
106
+ kurtosis: kurtosis,
107
+ excess_kurtosis: kurtosis ? kurtosis - 3 : nil,
108
+ }
109
+ rescue ArgumentError, TypeError, NoMethodError
110
+ # Outcomes are not compatible with each other, moments are fleeing.
111
+ {}
112
+ end
113
+
114
+ def moment(distribution, total_weight, degree, center = 0, variance = nil)
115
+ # With 0 variance, normalized moments are undefined.
116
+ return nil if variance == 0 # rubocop:disable Style/NumericPredicate
117
+
118
+ unnormalized = distribution.sum { |r, w| ((r - center)**degree) * Rational(w, total_weight) }
119
+ variance ? (unnormalized / (variance**(degree/2r))) : unnormalized
120
+ end
121
+ end
122
+ end
@@ -9,8 +9,8 @@ module Dicey
9
9
  # @raise [DiceyError] if +sides_list+ contains non-numerical values or is empty
10
10
  def initialize(sides_list)
11
11
  if Range === sides_list
12
- unless Numeric === sides_list.begin && Numeric === sides_list.end
13
- raise DiceyError, "`#{sides_list.inspect}` is not a numerical range!"
12
+ unless Integer === sides_list.begin && Integer === sides_list.end
13
+ raise DiceyError, "`#{sides_list.inspect}` is not a valid range!"
14
14
  end
15
15
  else
16
16
  sides_list.each do |value|
@@ -5,8 +5,20 @@ require_relative "key_value_formatter"
5
5
  module Dicey
6
6
  module OutputFormatters
7
7
  # Formats a hash as a text file suitable for consumption by Gnuplot.
8
+ #
9
+ # Will transform Rational probabilities to Floats.
8
10
  class GnuplotFormatter < KeyValueFormatter
9
11
  SEPARATOR = " "
12
+
13
+ private
14
+
15
+ def transform(key, value)
16
+ [derationalize(key), derationalize(value)]
17
+ end
18
+
19
+ def derationalize(value)
20
+ value.is_a?(Rational) ? value.to_f : value
21
+ end
10
22
  end
11
23
  end
12
24
  end
@@ -22,7 +22,10 @@ module Dicey
22
22
  private
23
23
 
24
24
  def to_primitive(value)
25
- primitive?(value) ? value : value.to_s
25
+ return value if primitive?(value)
26
+ return value.to_f if Numeric === value
27
+
28
+ value.to_s
26
29
  end
27
30
 
28
31
  def primitive?(value)
@@ -1,20 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../rational_to_integer"
4
+
3
5
  module Dicey
4
6
  module OutputFormatters
5
7
  # Base formatter for outputting lists of key-value pairs separated by newlines.
6
8
  # Can add an optional description into the result.
7
9
  # @abstract
8
10
  class KeyValueFormatter
11
+ include RationalToInteger
12
+
9
13
  # @param hash [Hash{Object => Object}]
10
14
  # @param description [String] text to add as a comment.
11
15
  # @return [String]
12
16
  def call(hash, description = nil)
13
17
  initial_string = description ? "# #{description}\n" : +""
14
18
  hash.each_with_object(initial_string) do |(key, value), output|
15
- output << "#{key}#{self.class::SEPARATOR}#{value}\n"
19
+ output << line(transform(key, value)) << "\n"
16
20
  end
17
21
  end
22
+
23
+ private
24
+
25
+ def transform(key, value)
26
+ [rational_to_integer(key), rational_to_integer(value)]
27
+ end
28
+
29
+ def line((key, value))
30
+ "#{key}#{self.class::SEPARATOR}#{value}"
31
+ end
18
32
  end
19
33
  end
20
34
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dicey
4
+ # Mix-in for converting rationals with denominator of 1 to integers.
5
+ module RationalToInteger
6
+ # Convert +value+ to +Integer+ if it's a +Rational+ with denominator of 1.
7
+ # Otherwise, return +value+ as-is.
8
+ #
9
+ # @value [Numeric, Any]
10
+ # @return [Numeric, Integer, Any]
11
+ def rational_to_integer(value)
12
+ (Rational === value && value.denominator == 1) ? value.numerator : value
13
+ end
14
+ end
15
+ end
data/lib/dicey/roller.rb CHANGED
@@ -2,9 +2,13 @@
2
2
 
3
3
  require_relative "die_foundry"
4
4
 
5
+ require_relative "rational_to_integer"
6
+
5
7
  module Dicey
6
8
  # Let the dice roll!
7
9
  class Roller
10
+ include RationalToInteger
11
+
8
12
  # @param arguments [Array<String>] die definitions
9
13
  # @param format [#call] formatter for output
10
14
  # @return [nil]
@@ -15,7 +19,7 @@ module Dicey
15
19
  dice = arguments.flat_map { |definition| die_foundry.cast(definition) }
16
20
  result = dice.sum(&:roll)
17
21
 
18
- format.call({ "roll" => result }, AbstractDie.describe(dice))
22
+ format.call({ "roll" => rational_to_integer(result) }, AbstractDie.describe(dice))
19
23
  end
20
24
 
21
25
  private
@@ -5,6 +5,14 @@ module Dicey
5
5
  module SumFrequencyCalculators
6
6
  # Base frequencies calculator.
7
7
  #
8
+ # *Result types:*
9
+ # - +:frequencies+ (default)
10
+ # - +:probabilities+
11
+ #
12
+ # By default, returns frequencies as they are easier to calculate and
13
+ # can be represented with integers.
14
+ # Probabilities are calculated using +Rational+ numbers to return exact results.
15
+ #
8
16
  # *Options:*
9
17
  #
10
18
  # Calculators may have calculator-specific options,
@@ -20,7 +28,7 @@ module Dicey
20
28
  # @param dice [Enumerable<AbstractDie>]
21
29
  # @param result_type [Symbol] one of {RESULT_TYPES}
22
30
  # @param options [Hash{Symbol => Any}] calculator-specific options
23
- # @return [Hash{Numeric => Numeric}] frequencies of each sum
31
+ # @return [Hash{Numeric => Numeric}] frequencies or probabilities for each outcome
24
32
  # @raise [DiceyError] if +result_type+ is invalid
25
33
  # @raise [DiceyError] if dice list is invalid for the calculator
26
34
  # @raise [DiceyError] if calculator returned obviously wrong results
@@ -92,7 +100,7 @@ module Dicey
92
100
  frequencies
93
101
  else
94
102
  total = frequencies.values.sum
95
- frequencies.transform_values { _1.fdiv(total) }
103
+ frequencies.transform_values { Rational(_1, total) }
96
104
  end
97
105
  end
98
106
  end
@@ -27,7 +27,7 @@ module Dicey
27
27
  def calculate(dice, rolls: N)
28
28
  statistics = rolls.times.with_object(Hash.new(0)) { |_, hash| hash[dice.sum(&:roll)] += 1 }
29
29
  total_results = dice.map(&:sides_num).reduce(:*)
30
- statistics.transform_values { (_1 * total_results).fdiv(rolls) }
30
+ statistics.transform_values { Rational(_1 * total_results, rolls) }
31
31
  end
32
32
 
33
33
  def verify_result(*)
data/lib/dicey/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dicey
4
- VERSION = "0.15.0"
4
+ VERSION = "0.15.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dicey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandr Bulancov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-22 00:00:00.000000000 Z
11
+ date: 2025-10-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Dicey provides a CLI executable and a Ruby API for fast calculation of
@@ -35,6 +35,7 @@ files:
35
35
  - lib/dicey/cli/blender.rb
36
36
  - lib/dicey/cli/options.rb
37
37
  - lib/dicey/die_foundry.rb
38
+ - lib/dicey/distribution_properties_calculator.rb
38
39
  - lib/dicey/numeric_die.rb
39
40
  - lib/dicey/output_formatters/gnuplot_formatter.rb
40
41
  - lib/dicey/output_formatters/hash_formatter.rb
@@ -42,6 +43,7 @@ files:
42
43
  - lib/dicey/output_formatters/key_value_formatter.rb
43
44
  - lib/dicey/output_formatters/list_formatter.rb
44
45
  - lib/dicey/output_formatters/yaml_formatter.rb
46
+ - lib/dicey/rational_to_integer.rb
45
47
  - lib/dicey/regular_die.rb
46
48
  - lib/dicey/roller.rb
47
49
  - lib/dicey/sum_frequency_calculators/base_calculator.rb
@@ -58,9 +60,9 @@ licenses:
58
60
  metadata:
59
61
  homepage_uri: https://github.com/trinistr/dicey
60
62
  bug_tracker_uri: https://github.com/trinistr/dicey/issues
61
- documentation_uri: https://rubydoc.info/gems/dicey/0.15.0
62
- source_code_uri: https://github.com/trinistr/dicey/tree/v0.15.0
63
- changelog_uri: https://github.com/trinistr/dicey/blob/v0.15.0/CHANGELOG.md
63
+ documentation_uri: https://rubydoc.info/gems/dicey/0.15.1
64
+ source_code_uri: https://github.com/trinistr/dicey/tree/v0.15.1
65
+ changelog_uri: https://github.com/trinistr/dicey/blob/v0.15.1/CHANGELOG.md
64
66
  rubygems_mfa_required: 'true'
65
67
  post_install_message:
66
68
  rdoc_options: