hanami-view 2.1.0.rc1 → 2.1.0.rc2

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/hanami-view.gemspec +0 -1
  4. data/lib/hanami/view/cache.rb +8 -1
  5. data/lib/hanami/view/context.rb +5 -0
  6. data/lib/hanami/view/decorated_attributes.rb +8 -0
  7. data/lib/hanami/view/erb/engine.rb +1 -1
  8. data/lib/hanami/view/erb/filters/block.rb +1 -1
  9. data/lib/hanami/view/erb/filters/trimming.rb +1 -1
  10. data/lib/hanami/view/erb/parser.rb +1 -1
  11. data/lib/hanami/view/erb/template.rb +1 -1
  12. data/lib/hanami/view/errors.rb +12 -20
  13. data/lib/hanami/view/exposure.rb +32 -0
  14. data/lib/hanami/view/exposures.rb +19 -0
  15. data/lib/hanami/view/helpers/escape_helper.rb +15 -15
  16. data/lib/hanami/view/helpers/number_formatting_helper.rb +13 -13
  17. data/lib/hanami/view/helpers/tag_helper/tag_builder.rb +21 -21
  18. data/lib/hanami/view/helpers/tag_helper.rb +8 -8
  19. data/lib/hanami/view/html.rb +14 -9
  20. data/lib/hanami/view/html_safe_string_buffer.rb +1 -1
  21. data/lib/hanami/view/part.rb +29 -12
  22. data/lib/hanami/view/part_builder.rb +6 -4
  23. data/lib/hanami/view/path.rb +14 -0
  24. data/lib/hanami/view/rendered.rb +13 -7
  25. data/lib/hanami/view/renderer.rb +17 -0
  26. data/lib/hanami/view/rendering.rb +17 -0
  27. data/lib/hanami/view/rendering_missing.rb +15 -0
  28. data/lib/hanami/view/scope.rb +23 -16
  29. data/lib/hanami/view/scope_builder.rb +5 -3
  30. data/lib/hanami/view/tilt/haml_adapter.rb +2 -0
  31. data/lib/hanami/view/tilt/slim_adapter.rb +2 -0
  32. data/lib/hanami/view/tilt.rb +5 -0
  33. data/lib/hanami/view/version.rb +3 -2
  34. data/lib/hanami/view.rb +69 -30
  35. metadata +3 -18
  36. data/lib/hanami/view/application_view.rb +0 -89
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cae3eed9f786bf94040d22363976ef28f957fb05ca8d91d0cb2f68510c0da09d
4
- data.tar.gz: f9619a535228de48ebd401597faae311f7c66acaface839b10530623ca2147b9
3
+ metadata.gz: 1f2fe4be0c1444d008d0a1f263b665469931fc7446ef53a581e40b9903b3d59b
4
+ data.tar.gz: 530f9ef13f2c7f30e31c6f47276dfb5b63280167b237af607616138216d5d47a
5
5
  SHA512:
6
- metadata.gz: 57141a4749d8bdc68066fc1a182cc193a1a032f04288e9de0c197c0f426a3ffa57cef7af90fef25a5994a79711cd669d29ca20e1376c1036b0ed39c0f544689e
7
- data.tar.gz: f2b295fa290d9e898fd924d9a1ef39cb73e986a1efe3df4bbbfa2b24ef3cea7bff298ca811c9e5a5948adafd1a84bab7644530b33099d21ecba9ed9a8b12cfaa
6
+ metadata.gz: 632c35a2934a68d891a6adafa04bf77692242c118c97ef67f67729047915ea2322e6d3d95a31fb56f207367d66f4dae844278caf3d3d98184ee8339d3762eb73
7
+ data.tar.gz: 7657dac57505121f31eea7f7c3a6dfaac22e157094353bcd5ad9baa62d52e32a687b7d697512fe99aef4baa500e37ea7fb6f053b872b9d3f08c28a17b3e49b60
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  View layer for Hanami
4
4
 
5
+ ## v2.1.0.rc2 - 2023-11-08
6
+
7
+ ### Changed
8
+ - [Tim Riley] Remove `LayoutNotFoundError` and raise `TemplateNotFoundError` for all kinds of missing templates, including layouts.
9
+
5
10
  ## v2.1.0.rc1 - 2023-11-01
6
11
 
7
12
  ### Fixed
data/hanami-view.gemspec CHANGED
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.required_ruby_version = ">= 3.0"
29
29
 
30
- spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
31
30
  spec.add_runtime_dependency "dry-configurable", "~> 1.0"
32
31
  spec.add_runtime_dependency "dry-core", "~> 1.0"
33
32
  spec.add_runtime_dependency "dry-inflector", "~> 1.0", "< 2"
@@ -4,10 +4,17 @@ require "dry/core/cache"
4
4
 
5
5
  module Hanami
6
6
  class View
7
- # @api private
7
+ # Shared cache for views.
8
+ #
9
+ # @api public
10
+ # @since 2.1.0
8
11
  class Cache
9
12
  extend Dry::Core::Cache
10
13
 
14
+ # Clears the view cache.
15
+ #
16
+ # @api public
17
+ # @since 2.1.0
11
18
  def self.clear
12
19
  cache.clear
13
20
  end
@@ -9,13 +9,16 @@ module Hanami
9
9
  # `#initialize` if you wish to inject dependencies)
10
10
  #
11
11
  # @api public
12
+ # @since 2.1.0
12
13
  class Context
13
14
  include DecoratedAttributes
14
15
 
15
16
  # @api private
17
+ # @since 2.1.0
16
18
  attr_reader :_rendering
17
19
 
18
20
  # @api private
21
+ # @since 2.1.0
19
22
  def self.new(rendering: RenderingMissing.new, **args)
20
23
  allocate.tap do |obj|
21
24
  obj.instance_variable_set(:@_rendering, rendering)
@@ -26,10 +29,12 @@ module Hanami
26
29
  # Returns a new instance of Context
27
30
  #
28
31
  # @api public
32
+ # @since 2.1.0
29
33
  def initialize(**)
30
34
  end
31
35
 
32
36
  # @api private
37
+ # @since 2.1.0
33
38
  def dup_for_rendering(rendering)
34
39
  dup.tap do |obj|
35
40
  obj.instance_variable_set(:@_rendering, rendering)
@@ -5,13 +5,20 @@ require "set"
5
5
  module Hanami
6
6
  class View
7
7
  # Decorates attributes in Parts.
8
+ #
9
+ # @api public
10
+ # @since 2.1.0
8
11
  module DecoratedAttributes
9
12
  # @api private
13
+ # @since 2.1.0
10
14
  def self.included(klass)
11
15
  klass.extend ClassInterface
12
16
  end
13
17
 
14
18
  # Decorated attributes class-level interface.
19
+ #
20
+ # @api public
21
+ # @since 2.1.0
15
22
  module ClassInterface
16
23
  # @api private
17
24
  MODULE_NAME = :DecoratedAttributes
@@ -31,6 +38,7 @@ module Hanami
31
38
  # matching Part
32
39
  #
33
40
  # @api public
41
+ # @since 2.1.0
34
42
  def decorate(*names, **options)
35
43
  decorated_attributes.decorate(*names, **options)
36
44
  end
@@ -8,7 +8,7 @@ module Hanami
8
8
  # Hanami::View ERB engine.
9
9
  #
10
10
  # @api private
11
- # @since 2.0.0
11
+ # @since 2.1.0
12
12
  class Engine < Temple::Engine
13
13
  define_options capture_generator: Hanami::View::HTMLSafeStringBuffer
14
14
 
@@ -9,7 +9,7 @@ module Hanami
9
9
  #
10
10
  # Inspired by Slim's Slim::Controls::Filter#on_slim_output.
11
11
  #
12
- # @since 2.0.0
12
+ # @since 2.1.0
13
13
  # @api private
14
14
  class Block < Temple::Filter
15
15
  END_LINE_RE = /\bend\b/
@@ -18,7 +18,7 @@ module Hanami
18
18
  # `:multi` sexps with their own nested content.
19
19
  #
20
20
  # @api private
21
- # @since 2.0.0
21
+ # @since 2.1.0
22
22
  class Trimming < Temple::Filter
23
23
  define_options trim: true
24
24
 
@@ -78,7 +78,7 @@ module Hanami
78
78
  # remain, which is the value returned.
79
79
  #
80
80
  # @api private
81
- # @since 2.0.0
81
+ # @since 2.1.0
82
82
  class Parser < Temple::Parser
83
83
  ERB_PATTERN = /(\n|<%%|%%>)|<%(==?|\#)?(.*?)?-?%>/m
84
84
 
@@ -21,7 +21,7 @@ module Hanami
21
21
  # Hanami::View::ERB::Template.new { "<%= 'Hello, world!' %>" }.render
22
22
  #
23
23
  # @api private
24
- # @since 2.0.0
24
+ # @since 2.1.0
25
25
  module ERB
26
26
  # ERB Template class
27
27
  Template = Temple::Templates::Tilt(Engine)
@@ -2,24 +2,27 @@
2
2
 
3
3
  module Hanami
4
4
  class View
5
- # @since 2.0.0
5
+ # Base error for views.
6
+ #
7
+ # @since 2.1.0
6
8
  # @api public
7
9
  class Error < StandardError
8
10
  end
9
11
 
10
- # Error raised when critical settings are not configured
12
+ # Error raised when critical settings are not configured.
11
13
  #
12
- # @api private
14
+ # @api public
15
+ # @since 2.1.0
13
16
  class UndefinedConfigError < StandardError
14
17
  def initialize(key)
15
18
  super("no +#{key}+ configured")
16
19
  end
17
20
  end
18
21
 
19
- # Error raised when template could not be found within a view's configured
20
- # paths
22
+ # Error raised when template could not be found within a view's configured paths.
21
23
  #
22
- # @api private
24
+ # @api public
25
+ # @since 2.1.0
23
26
  class TemplateNotFoundError < StandardError
24
27
  def initialize(template_name, format, lookup_paths)
25
28
  msg = [
@@ -31,21 +34,10 @@ module Hanami
31
34
  end
32
35
  end
33
36
 
34
- # Error raised when layout could not be found within a view's configured
35
- # paths
37
+ # Error raised when a rendering is required but not given.
36
38
  #
37
- # @api private
38
- class LayoutNotFoundError < StandardError
39
- def initialize(layout_name, lookup_paths)
40
- msg = [
41
- "Layout +#{layout_name}+ could not be found in paths:",
42
- lookup_paths.map { |path| " - #{path}" }
43
- ].join("\n\n")
44
-
45
- super(msg)
46
- end
47
- end
48
-
39
+ # @api public
40
+ # @since 2.1.0
49
41
  class RenderingMissingError < Error
50
42
  def message
51
43
  "a +rendering+ must be provided"
@@ -7,17 +7,31 @@ module Hanami
7
7
  # An exposure defined on a view
8
8
  #
9
9
  # @api private
10
+ # @since 2.1.0
10
11
  class Exposure
11
12
  include Dry::Equalizer(:name, :proc, :object, :options)
12
13
 
13
14
  EXPOSURE_DEPENDENCY_PARAMETER_TYPES = %i[req opt].freeze
14
15
  INPUT_PARAMETER_TYPES = %i[key keyreq keyrest].freeze
15
16
 
17
+ # @api private
18
+ # @since 2.1.0
16
19
  attr_reader :name
20
+
21
+ # @api private
22
+ # @since 2.1.0
17
23
  attr_reader :proc
24
+
25
+ # @api private
26
+ # @since 2.1.0
18
27
  attr_reader :object
28
+
29
+ # @api private
30
+ # @since 2.1.0
19
31
  attr_reader :options
20
32
 
33
+ # @api private
34
+ # @since 2.1.0
21
35
  def initialize(name, proc = nil, object = nil, **options)
22
36
  @name = name
23
37
  @proc = prepare_proc(proc, object)
@@ -25,10 +39,14 @@ module Hanami
25
39
  @options = options
26
40
  end
27
41
 
42
+ # @api private
43
+ # @since 2.1.0
28
44
  def bind(obj)
29
45
  self.class.new(name, proc, obj, **options)
30
46
  end
31
47
 
48
+ # @api private
49
+ # @since 2.1.0
32
50
  def dependency_names
33
51
  @dependency_names ||=
34
52
  if proc
@@ -40,10 +58,14 @@ module Hanami
40
58
  end
41
59
  end
42
60
 
61
+ # @api private
62
+ # @since 2.1.0
43
63
  def dependencies?
44
64
  !dependency_names.empty?
45
65
  end
46
66
 
67
+ # @api private
68
+ # @since 2.1.0
47
69
  def input_keys
48
70
  @input_keys ||=
49
71
  if proc
@@ -55,22 +77,32 @@ module Hanami
55
77
  end
56
78
  end
57
79
 
80
+ # @api private
81
+ # @since 2.1.0
58
82
  def for_layout?
59
83
  options.fetch(:layout, false)
60
84
  end
61
85
 
86
+ # @api private
87
+ # @since 2.1.0
62
88
  def decorate?
63
89
  options.fetch(:decorate, true)
64
90
  end
65
91
 
92
+ # @api private
93
+ # @since 2.1.0
66
94
  def private?
67
95
  options.fetch(:private, false)
68
96
  end
69
97
 
98
+ # @api private
99
+ # @since 2.1.0
70
100
  def default_value
71
101
  options[:default]
72
102
  end
73
103
 
104
+ # @api private
105
+ # @since 2.1.0
74
106
  def call(input, locals = {})
75
107
  if proc
76
108
  call_proc(input, locals)
@@ -6,36 +6,53 @@ require "dry/core/equalizer"
6
6
  module Hanami
7
7
  class View
8
8
  # @api private
9
+ # @since 2.1.0
9
10
  class Exposures
10
11
  include Dry::Equalizer(:exposures)
11
12
  include TSort
12
13
 
14
+ # @api private
15
+ # @since 2.1.0
13
16
  attr_reader :exposures
14
17
 
18
+ # @api private
19
+ # @since 2.1.0
15
20
  def initialize(exposures = {})
16
21
  @exposures = exposures
17
22
  end
18
23
 
24
+ # @api private
25
+ # @since 2.1.0
19
26
  def key?(name)
20
27
  exposures.key?(name)
21
28
  end
22
29
 
30
+ # @api private
31
+ # @since 2.1.0
23
32
  def [](name)
24
33
  exposures[name]
25
34
  end
26
35
 
36
+ # @api private
37
+ # @since 2.1.0
27
38
  def each(&block)
28
39
  exposures.each(&block)
29
40
  end
30
41
 
42
+ # @api private
43
+ # @since 2.1.0
31
44
  def add(name, proc = nil, **options)
32
45
  exposures[name] = Exposure.new(name, proc, **options)
33
46
  end
34
47
 
48
+ # @api private
49
+ # @since 2.1.0
35
50
  def import(name, exposure)
36
51
  exposures[name] = exposure.dup
37
52
  end
38
53
 
54
+ # @api private
55
+ # @since 2.1.0
39
56
  def bind(obj)
40
57
  bound_exposures = exposures.each_with_object({}) { |(name, exposure), memo|
41
58
  memo[name] = exposure.bind(obj)
@@ -44,6 +61,8 @@ module Hanami
44
61
  self.class.new(bound_exposures)
45
62
  end
46
63
 
64
+ # @api private
65
+ # @since 2.1.0
47
66
  def call(input)
48
67
  # Avoid performance cost of tsorting when we don't need it
49
68
  names =
@@ -29,7 +29,7 @@ module Hanami
29
29
  # end
30
30
  #
31
31
  # @api public
32
- # @since 2.0.0
32
+ # @since 2.1.0
33
33
  module EscapeHelper
34
34
  extend self
35
35
 
@@ -56,13 +56,13 @@ module Hanami
56
56
  # # => "<p>Not escaped</p>"
57
57
  #
58
58
  # @api public
59
- # @since 2.0.0
59
+ # @since 2.1.0
60
60
  def escape_html(input)
61
61
  Temple::Utils.escape_html_safe(input)
62
62
  end
63
63
 
64
64
  # @api public
65
- # @since 2.0.0
65
+ # @since 2.1.0
66
66
  alias_method :h, :escape_html
67
67
 
68
68
  # Returns an escaped string from joining the elements in a given array.
@@ -86,7 +86,7 @@ module Hanami
86
86
  # @see #escape_html
87
87
  #
88
88
  # @api public
89
- # @since 2.0.0
89
+ # @since 2.1.0
90
90
  def escape_join(array, separator = $,)
91
91
  separator = escape_html(separator)
92
92
 
@@ -116,7 +116,7 @@ module Hanami
116
116
  # # => "gemini://gemini.circumlunar.space/"
117
117
  #
118
118
  # @api public
119
- # @since 2.0.0
119
+ # @since 2.1.0
120
120
  def sanitize_url(input, permitted_schemes = PERMITTED_URL_SCHEMES)
121
121
  return input if input.html_safe?
122
122
 
@@ -127,7 +127,7 @@ module Hanami
127
127
  end
128
128
 
129
129
  # @api private
130
- # @since 2.0.0
130
+ # @since 2.1.0
131
131
  PERMITTED_URL_SCHEMES = %w[http https mailto].freeze
132
132
  private_constant :PERMITTED_URL_SCHEMES
133
133
 
@@ -142,7 +142,7 @@ module Hanami
142
142
  # escape_xml_name("1 < 2 & 3") # => "1___2___3"
143
143
  #
144
144
  # @api public
145
- # @since 2.0.0
145
+ # @since 2.1.0
146
146
  def escape_xml_name(name)
147
147
  name = name.to_s
148
148
  return "" if name.match?(BLANK_STRING_REGEXP)
@@ -160,12 +160,12 @@ module Hanami
160
160
  end
161
161
 
162
162
  # @api private
163
- # @since 2.0.0
163
+ # @since 2.1.0
164
164
  BLANK_STRING_REGEXP = /\A\s*\z/
165
165
 
166
166
  # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
167
167
  # @api private
168
- # @since 2.0.0
168
+ # @since 2.1.0
169
169
  TAG_NAME_START_CODEPOINTS = \
170
170
  "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
171
171
  "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
@@ -173,27 +173,27 @@ module Hanami
173
173
  private_constant :TAG_NAME_START_CODEPOINTS
174
174
 
175
175
  # @api private
176
- # @since 2.0.0
176
+ # @since 2.1.0
177
177
  INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
178
178
  private_constant :INVALID_TAG_NAME_START_REGEXP
179
179
 
180
180
  # @api private
181
- # @since 2.0.0
181
+ # @since 2.1.0
182
182
  TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
183
183
  private_constant :TAG_NAME_FOLLOWING_CODEPOINTS
184
184
 
185
185
  # @api private
186
- # @since 2.0.0
186
+ # @since 2.1.0
187
187
  INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
188
188
  private_constant :INVALID_TAG_NAME_FOLLOWING_REGEXP
189
189
 
190
190
  # @api private
191
- # @since 2.0.0
191
+ # @since 2.1.0
192
192
  SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
193
193
  private_constant :INVALID_TAG_NAME_FOLLOWING_REGEXP
194
194
 
195
195
  # @api private
196
- # @since 2.0.0
196
+ # @since 2.1.0
197
197
  TAG_NAME_REPLACEMENT_CHAR = "_"
198
198
  private_constant :TAG_NAME_REPLACEMENT_CHAR
199
199
 
@@ -211,7 +211,7 @@ module Hanami
211
211
  # raw(user.name).html_safe? # => true
212
212
  #
213
213
  # @api public
214
- # @since 2.0.0
214
+ # @since 2.1.0
215
215
  def raw(input)
216
216
  input.to_s.html_safe
217
217
  end
@@ -26,7 +26,7 @@ module Hanami
26
26
  # end
27
27
  #
28
28
  # @api public
29
- # @since 2.0.0
29
+ # @since 2.1.0
30
30
  module NumberFormattingHelper
31
31
  extend self
32
32
 
@@ -34,7 +34,7 @@ module Hanami
34
34
  #
35
35
  # @return [String] default delimiter
36
36
  #
37
- # @since 2.0.0
37
+ # @since 2.1.0
38
38
  # @api private
39
39
  DEFAULT_DELIMITER = ","
40
40
  private_constant :DEFAULT_DELIMITER
@@ -43,7 +43,7 @@ module Hanami
43
43
  #
44
44
  # @return [String] default separator
45
45
  #
46
- # @since 2.0.0
46
+ # @since 2.1.0
47
47
  # @api private
48
48
  DEFAULT_SEPARATOR = "."
49
49
  private_constant :DEFAULT_SEPARATOR
@@ -52,7 +52,7 @@ module Hanami
52
52
  #
53
53
  # @return [Integer] default rounding precision
54
54
  #
55
- # @since 2.0.0
55
+ # @since 2.1.0
56
56
  # @api private
57
57
  DEFAULT_PRECISION = 2
58
58
  private_constant :DEFAULT_PRECISION
@@ -82,7 +82,7 @@ module Hanami
82
82
  # format_number(1256.95, delimiter: ".", separator: ",") # => "1.256,95"
83
83
  #
84
84
  # @api public
85
- # @since 2.0.0
85
+ # @since 2.1.0
86
86
  def format_number(number, delimiter: DEFAULT_DELIMITER, separator: DEFAULT_SEPARATOR, precision: DEFAULT_PRECISION) # rubocop:disable Layout/LineLength
87
87
  Formatter.call(number, delimiter: delimiter, separator: separator, precision: precision)
88
88
  end
@@ -91,26 +91,26 @@ module Hanami
91
91
 
92
92
  # Formatter
93
93
  #
94
- # @since 2.0.0
94
+ # @since 2.1.0
95
95
  # @api private
96
96
  class Formatter
97
97
  # Regex to delimit the integer part of a number into groups of three digits.
98
98
  #
99
- # @since 2.0.0
99
+ # @since 2.1.0
100
100
  # @api private
101
101
  DELIMITING_REGEX = /(\d)(?=(\d{3})+$)/
102
102
  private_constant :DELIMITING_REGEX
103
103
 
104
104
  # Regex to guess if the number is a integer.
105
105
  #
106
- # @since 2.0.0
106
+ # @since 2.1.0
107
107
  # @api private
108
108
  INTEGER_REGEXP = /\A\d+\z/
109
109
  private_constant :INTEGER_REGEXP
110
110
 
111
111
  # @see NumberFormattingHelper#format_number
112
112
  #
113
- # @since 2.0.0
113
+ # @since 2.1.0
114
114
  # @api private
115
115
  def self.call(number, delimiter:, separator:, precision:)
116
116
  number = coerce(number)
@@ -122,7 +122,7 @@ module Hanami
122
122
 
123
123
  # Coerces the given number or string into a number.
124
124
  #
125
- # @since 2.0.0
125
+ # @since 2.1.0
126
126
  # @api private
127
127
  def self.coerce(number)
128
128
  case number
@@ -143,7 +143,7 @@ module Hanami
143
143
 
144
144
  # Formats the given number as a string.
145
145
  #
146
- # @since 2.0.0
146
+ # @since 2.1.0
147
147
  # @api private
148
148
  def self.to_str(number, precision)
149
149
  case number
@@ -156,7 +156,7 @@ module Hanami
156
156
 
157
157
  # Returns the integer and fractional parts of the given number string.
158
158
  #
159
- # @since 2.0.0
159
+ # @since 2.1.0
160
160
  # @api private
161
161
  def self.parts(string, delimiter)
162
162
  integer_part, fractional_part = string.split(DEFAULT_SEPARATOR)
@@ -170,7 +170,7 @@ module Hanami
170
170
  #
171
171
  # @return [String] delimited integer string
172
172
  #
173
- # @since 2.0.0
173
+ # @since 2.1.0
174
174
  # @api private
175
175
  def self.delimit_integer(integer_part, delimiter)
176
176
  integer_part.gsub(DELIMITING_REGEX) { |digit| "#{digit}#{delimiter}" }