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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/hanami-view.gemspec +0 -1
- data/lib/hanami/view/cache.rb +8 -1
- data/lib/hanami/view/context.rb +5 -0
- data/lib/hanami/view/decorated_attributes.rb +8 -0
- data/lib/hanami/view/erb/engine.rb +1 -1
- data/lib/hanami/view/erb/filters/block.rb +1 -1
- data/lib/hanami/view/erb/filters/trimming.rb +1 -1
- data/lib/hanami/view/erb/parser.rb +1 -1
- data/lib/hanami/view/erb/template.rb +1 -1
- data/lib/hanami/view/errors.rb +12 -20
- data/lib/hanami/view/exposure.rb +32 -0
- data/lib/hanami/view/exposures.rb +19 -0
- data/lib/hanami/view/helpers/escape_helper.rb +15 -15
- data/lib/hanami/view/helpers/number_formatting_helper.rb +13 -13
- data/lib/hanami/view/helpers/tag_helper/tag_builder.rb +21 -21
- data/lib/hanami/view/helpers/tag_helper.rb +8 -8
- data/lib/hanami/view/html.rb +14 -9
- data/lib/hanami/view/html_safe_string_buffer.rb +1 -1
- data/lib/hanami/view/part.rb +29 -12
- data/lib/hanami/view/part_builder.rb +6 -4
- data/lib/hanami/view/path.rb +14 -0
- data/lib/hanami/view/rendered.rb +13 -7
- data/lib/hanami/view/renderer.rb +17 -0
- data/lib/hanami/view/rendering.rb +17 -0
- data/lib/hanami/view/rendering_missing.rb +15 -0
- data/lib/hanami/view/scope.rb +23 -16
- data/lib/hanami/view/scope_builder.rb +5 -3
- data/lib/hanami/view/tilt/haml_adapter.rb +2 -0
- data/lib/hanami/view/tilt/slim_adapter.rb +2 -0
- data/lib/hanami/view/tilt.rb +5 -0
- data/lib/hanami/view/version.rb +3 -2
- data/lib/hanami/view.rb +69 -30
- metadata +3 -18
- data/lib/hanami/view/application_view.rb +0 -89
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f2fe4be0c1444d008d0a1f263b665469931fc7446ef53a581e40b9903b3d59b
|
|
4
|
+
data.tar.gz: 530f9ef13f2c7f30e31c6f47276dfb5b63280167b237af607616138216d5d47a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 632c35a2934a68d891a6adafa04bf77692242c118c97ef67f67729047915ea2322e6d3d95a31fb56f207367d66f4dae844278caf3d3d98184ee8339d3762eb73
|
|
7
|
+
data.tar.gz: 7657dac57505121f31eea7f7c3a6dfaac22e157094353bcd5ad9baa62d52e32a687b7d697512fe99aef4baa500e37ea7fb6f053b872b9d3f08c28a17b3e49b60
|
data/CHANGELOG.md
CHANGED
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"
|
data/lib/hanami/view/cache.rb
CHANGED
|
@@ -4,10 +4,17 @@ require "dry/core/cache"
|
|
|
4
4
|
|
|
5
5
|
module Hanami
|
|
6
6
|
class View
|
|
7
|
-
#
|
|
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
|
data/lib/hanami/view/context.rb
CHANGED
|
@@ -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
|
data/lib/hanami/view/errors.rb
CHANGED
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
module Hanami
|
|
4
4
|
class View
|
|
5
|
-
#
|
|
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
|
|
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
|
|
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
|
|
35
|
-
# paths
|
|
37
|
+
# Error raised when a rendering is required but not given.
|
|
36
38
|
#
|
|
37
|
-
# @api
|
|
38
|
-
|
|
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"
|
data/lib/hanami/view/exposure.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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}" }
|