hanami-utils 1.3.7 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '02818d90409ea03d89ca587c97fe418bbf17a8694662a2313609c0710466b0f9'
4
- data.tar.gz: 973ef420992dd9ea30ab880d43e8cc70eee59cae6fb7b1ba17038c5ce8d536a4
3
+ metadata.gz: 2d4523d52299280469a80451592c55d5771aeaf8e0f93644291f5e9d7a66237a
4
+ data.tar.gz: 27c144da73fcbd992b4fefdddb9dc1b5be1e58b5abff3fd4078c67f8ec561d43
5
5
  SHA512:
6
- metadata.gz: d49d6ad21aa25f285c9165c4619d6b2d7fe348513481bf30b8f231211d4e270f7502e2e3594f082789fdf32e9170a33e038329394ae5f43170ab778ebe01892f
7
- data.tar.gz: 8b8832a39855525d6818de385212fb1c9d7c5c0f79874f60ead95ee1c592377f6bfec0ade5701a4340b87fe920c495889a9586d81fe2091d8691b52b14f297fa
6
+ metadata.gz: e0e48686597a78927e05bff5da29e0ba0db65f8a03796b36046bb6f84cf35bf964662a87a73bad88bca47096b2b51e737bee0b0545f7341cbbd3579c4e74d4bf
7
+ data.tar.gz: ebf2deed3ccf571a88ffd151d544254d483f65f26c5bf027c78cbe31220b22afc6b0c9f5b7a6223dd685c553b41fb80c2fe4a7b2a3629d4ab1cdfc5f9293e038
@@ -1,37 +1,22 @@
1
1
  # Hanami::Utils
2
2
  Ruby core extentions and class utilities for Hanami
3
3
 
4
- ## v1.3.7 - 2021-01-04
4
+ ## v2.0.0.alpha1 - 2019-01-30
5
5
  ### Added
6
- - [Luca Guidi] Official support for Ruby: MRI 3.0
7
- - [Khai Le] Allow `Hanami::Logger` to filter sensitive data for an array of hashes
6
+ - [Gustavo Caso] Introduce `Hanami::Middleware` namespace
7
+ - [Luca Guidi] Introduce `Callbacks::Chain#dup`
8
8
 
9
- ### Fixed
10
- - [Hiếu Nguyễn] Ensure `Hanami::Logger` to not mutate `Hash` input when filtering sensitive data
11
-
12
- ## v1.3.6 - 2020-01-07
13
- ### Added
14
- - [Luca Guidi] Official support for Ruby: MRI 2.7
15
-
16
- ### Fixed
17
- - [ippachi] `Utils::Files.append`: don't check breakline if file is empty
18
-
19
- ## v1.3.5 - 2019-10-25
20
- ### Fixed
21
- - [Ivan Kabluchkov] Ensure `Hanami::Logger` filters to not crash when logger stream is a closed tempfile
22
- - [Luca Guidi] Ensure `Utils::Files.append` to append contents properly when existing file doesn't end with a newline
23
-
24
- ## v1.3.4 - 2019-09-27
25
- ### Added
26
- - [Luca Guidi] Let `Utils::BasicObject` to lookup constants at the top-level namespace
27
-
28
- ## v1.3.3 - 2019-09-13
29
- ### Fixed
30
- - [Mauro Morales] Ensure `Utils::Inflector.pluralize` and `.singularize` to work with words that contain an underscore (`_`)
31
-
32
- ## v1.3.2 - 2019-06-21
33
- ### Added
34
- - [Vladislav Yashin & Luca Guidi] Added `Utils::BasicObject#instance_of?`, `#is_a?`, and `#kind_of`
9
+ ### Changed
10
+ - [Luca Guidi] Drop support for Ruby: MRI 2.3, and 2.4.
11
+ - [Luca Guidi] Remove `Utils::Duplicable`
12
+ - [Luca Guidi] Remove `Utils::Inflector`
13
+ - [Luca Guidi] Remove `Utils::String.singularize`, and `.pluralize`
14
+ - [Luca Guidi] Remove `Utils::String#singularize`, and `#pluralize`
15
+ - [Luca Guidi] Remove instance level interface for `Utils::Hash`
16
+ - [Luca Guidi] Transform `Utils::Hash` from class to module
17
+ - [Luca Guidi] Remove `Utils.reload!`
18
+ - [Gustavo Caso] Remove `Utils::File.rewrite`
19
+ - [Vladimir Suvorov] Remove `Utils::Class.load_from_pattern!`
35
20
 
36
21
  ## v1.3.1 - 2019-01-18
37
22
  ### Added
data/README.md CHANGED
@@ -5,7 +5,8 @@ Ruby core extensions and class utilities for [Hanami](http://hanamirb.org)
5
5
  ## Status
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/hanami-utils.svg)](https://badge.fury.io/rb/hanami-utils)
8
- [![CI](https://github.com/hanami/utils/workflows/ci/badge.svg?branch=master)](https://github.com/hanami/utils/actions?query=workflow%3Aci+branch%3Amaster)
8
+ [![TravisCI](https://travis-ci.org/hanami/utils.svg?branch=master)](https://travis-ci.org/hanami/utils)
9
+ [![CircleCI](https://circleci.com/gh/hanami/utils/tree/master.svg?style=svg)](https://circleci.com/gh/hanami/utils/tree/master)
9
10
  [![Test Coverage](https://codecov.io/gh/hanami/utils/branch/master/graph/badge.svg)](https://codecov.io/gh/hanami/utils)
10
11
  [![Depfu](https://badges.depfu.com/badges/a8545fb67cf32a2c75b6227bc0821027/overview.svg)](https://depfu.com/github/hanami/utils?project=Bundler)
11
12
  [![Inline Docs](http://inch-ci.org/github/hanami/utils.svg)](http://inch-ci.org/github/hanami/utils)
@@ -21,7 +22,7 @@ Ruby core extensions and class utilities for [Hanami](http://hanamirb.org)
21
22
 
22
23
  ## Rubies
23
24
 
24
- __Hanami::Utils__ supports Ruby (MRI) 2.3+, JRuby 9.1.5.0+
25
+ __Hanami::Utils__ supports Ruby (MRI) 2.5+
25
26
 
26
27
  ## Installation
27
28
 
@@ -78,11 +79,6 @@ Inheritable class attributes. [[API doc](http://www.rubydoc.info/gems/hanami-uti
78
79
 
79
80
  Deprecate Hanami features. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Deprecation)]
80
81
 
81
- ### Hanami::Utils::Duplicable
82
-
83
- Safe `#dup` logic for Ruby objects. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Duplicable)]
84
-
85
-
86
82
  ### Hanami::Utils::Escape
87
83
 
88
84
  Safe and fast escape for URLs, HTML content and attributes. Based on OWASP/ESAPI code. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Escape)]
@@ -97,16 +93,12 @@ File utilities to manipulate files and directories. [[API doc](http://www.rubydo
97
93
 
98
94
  ### Hanami::Utils::Hash
99
95
 
100
- Enhanced version of Ruby's `Hash`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Hash)]
96
+ `Hash` transformations. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Hash)]
101
97
 
102
98
  ### Hanami::Utils::IO
103
99
 
104
100
  Silence Ruby warnings. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/IO)]
105
101
 
106
- ### Hanami::Utils::Inflector
107
-
108
- Complete and customizable english inflections (pluralization and singularization). [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Inflector)]
109
-
110
102
  ### Hanami::Utils::Json
111
103
 
112
104
  JSON engine with swappable backends (via optional `multi_json` gem) or powered by Ruby's `json` (default). [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Json)]
@@ -149,6 +141,6 @@ __Hanami::Utils__ uses [Semantic Versioning 2.0.0](http://semver.org)
149
141
 
150
142
  ## Copyright
151
143
 
152
- Copyright © 2014-2021 Luca Guidi – Released under MIT License
144
+ Copyright © 2014-2019 Luca Guidi – Released under MIT License
153
145
 
154
146
  This project was formerly known as Lotus (`lotus-utils`).
@@ -18,13 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ["lib"]
21
- spec.required_ruby_version = ">= 2.3.0"
21
+ spec.required_ruby_version = ">= 2.5.0"
22
22
 
23
23
  spec.add_dependency "transproc", "~> 1.0"
24
24
  spec.add_dependency "concurrent-ruby", "~> 1.0"
25
25
 
26
26
  spec.add_development_dependency "bundler", ">= 1.6", "< 3"
27
- spec.add_development_dependency "rake", "~> 13"
28
- spec.add_development_dependency "rspec", "~> 3.9"
29
- spec.add_development_dependency "rubocop", "0.81" # rubocop 0.81+ removed support for Ruby 2.3
27
+ spec.add_development_dependency "rake", "~> 12"
28
+ spec.add_development_dependency "rspec", "~> 3.7"
30
29
  end
@@ -19,14 +19,14 @@ module Hanami
19
19
  # @api private
20
20
  #
21
21
  # @see Hanami::Interactor::Result#respond_to_missing?
22
- METHODS = ::Hash[initialize: true,
23
- success?: true,
22
+ METHODS = ::Hash[initialize: true,
23
+ success?: true,
24
24
  successful?: true,
25
- failure?: true,
26
- fail!: true,
27
- prepare!: true,
28
- errors: true,
29
- error: true].freeze
25
+ failure?: true,
26
+ fail!: true,
27
+ prepare!: true,
28
+ errors: true,
29
+ error: true].freeze
30
30
 
31
31
  # Initialize a new result
32
32
  #
@@ -42,7 +42,7 @@ module Hanami
42
42
  @success = true
43
43
  end
44
44
 
45
- # Checks if the current status is successful
45
+ # Check if the current status is successful
46
46
  #
47
47
  # @return [TrueClass,FalseClass] the result of the check
48
48
  #
@@ -52,9 +52,9 @@ module Hanami
52
52
  end
53
53
 
54
54
  # @since 0.3.5
55
- alias_method :success?, :successful?
55
+ alias success? successful?
56
56
 
57
- # Checks if the current status is not successful
57
+ # Check if the current status is not successful
58
58
  #
59
59
  # @return [TrueClass,FalseClass] the result of the check
60
60
  #
@@ -63,7 +63,7 @@ module Hanami
63
63
  !successful?
64
64
  end
65
65
 
66
- # Forces the status to be a failure
66
+ # Force the status to be a failure
67
67
  #
68
68
  # @since 0.3.5
69
69
  def fail!
@@ -106,7 +106,7 @@ module Hanami
106
106
  errors.first
107
107
  end
108
108
 
109
- # Prepares the result before to be returned
109
+ # Prepare the result before to be returned
110
110
  #
111
111
  # @param payload [Hash] an updated payload
112
112
  #
@@ -353,57 +353,33 @@ module Hanami
353
353
  # end
354
354
  #
355
355
  # Signup.new.call # => NoMethodError
356
- if RUBY_VERSION >= "3.0"
357
- def call(*args, **kwargs)
358
- @__result = ::Hanami::Interactor::Result.new
359
- _call(*args, **kwargs) { super }
360
- end
361
- else
362
- def call(*args)
363
- @__result = ::Hanami::Interactor::Result.new
364
- _call(*args) { super }
365
- end
356
+ def call(*args)
357
+ @__result = ::Hanami::Interactor::Result.new
358
+ _call(*args) { super }
366
359
  end
367
360
 
368
361
  private
369
362
 
370
363
  # @api private
371
364
  # @since 1.1.0
372
- if RUBY_VERSION >= "3.0"
373
- def _call(*args, **kwargs)
374
- catch :fail do
375
- validate!(*args, **kwargs)
376
- yield
377
- end
378
-
379
- _prepare!
365
+ def _call(*args)
366
+ catch :fail do
367
+ validate!(*args)
368
+ yield
380
369
  end
381
- else
382
- def _call(*args)
383
- catch :fail do
384
- validate!(*args)
385
- yield
386
- end
387
370
 
388
- _prepare!
389
- end
371
+ _prepare!
390
372
  end
391
373
 
392
374
  # @since 1.1.0
393
- if RUBY_VERSION >= "3.0"
394
- def validate!(*args, **kwargs)
395
- fail! unless valid?(*args, **kwargs)
396
- end
397
- else
398
- def validate!(*args)
399
- fail! unless valid?(*args)
400
- end
375
+ def validate!(*args)
376
+ fail! unless valid?(*args)
401
377
  end
402
378
  end
403
379
 
404
380
  private
405
381
 
406
- # Checks if proceed with <tt>#call</tt> invocation.
382
+ # Check if proceed with <tt>#call</tt> invokation.
407
383
  # By default it returns <tt>true</tt>.
408
384
  #
409
385
  # Developers can override it.
@@ -415,7 +391,7 @@ module Hanami
415
391
  true
416
392
  end
417
393
 
418
- # Fails and interrupts the current flow.
394
+ # Fail and interrupt the current flow.
419
395
  #
420
396
  # @since 0.3.5
421
397
  #
@@ -454,7 +430,7 @@ module Hanami
454
430
  throw :fail
455
431
  end
456
432
 
457
- # Logs an error without interrupting the flow.
433
+ # Log an error without interrupting the flow.
458
434
  #
459
435
  # When used, the returned result won't be successful.
460
436
  #
@@ -509,7 +485,7 @@ module Hanami
509
485
  false
510
486
  end
511
487
 
512
- # Logs an error and interrupts the flow.
488
+ # Log an error AND interrupting the flow.
513
489
  #
514
490
  # When used, the returned result won't be successful.
515
491
  #
@@ -572,7 +548,7 @@ module Hanami
572
548
  # @since 0.5.0
573
549
  # @api private
574
550
  def _exposures
575
- Hash[].tap do |result|
551
+ ::Hash[].tap do |result|
576
552
  self.class.exposures.each do |name, ivar|
577
553
  result[name] = instance_variable_defined?(ivar) ? instance_variable_get(ivar) : nil
578
554
  end
@@ -605,7 +581,7 @@ module Hanami
605
581
  end
606
582
  end
607
583
 
608
- # Exposes local instance variables into the returning value of <tt>#call</tt>
584
+ # Expose local instance variables into the returning value of <tt>#call</tt>
609
585
  #
610
586
  # @param instance_variable_names [Symbol,Array<Symbol>] one or more instance
611
587
  # variable names
@@ -614,7 +590,7 @@ module Hanami
614
590
  #
615
591
  # @see Hanami::Interactor::Result
616
592
  #
617
- # @example Exposes instance variable
593
+ # @example Expose instance variable
618
594
  #
619
595
  # class Signup
620
596
  # include Hanami::Interactor
@@ -7,7 +7,7 @@ require "hanami/utils/files"
7
7
  module Hanami
8
8
  # Hanami logger
9
9
  #
10
- # Implementation with the same interface of Ruby std lib `Logger`.
10
+ # Implement with the same interface of Ruby std lib `Logger`.
11
11
  # It uses `STDOUT`, `STDERR`, file name or open file as output stream.
12
12
  #
13
13
  #
@@ -16,7 +16,7 @@ module Hanami
16
16
  #
17
17
  # This is useful for auto-tagging the output. Eg (`app=Booshelf`).
18
18
  #
19
- # When used standalone (eg. `Hanami::Logger.info`), it tags lines with `app=Shared`.
19
+ # When used stand alone (eg. `Hanami::Logger.info`), it tags lines with `app=Shared`.
20
20
  #
21
21
  #
22
22
  # The available severity levels are the same of `Logger`:
@@ -30,7 +30,7 @@ module Hanami
30
30
  #
31
31
  # Those levels are available both as class and instance methods.
32
32
  #
33
- # Also Hanami::Logger supports different formatters. Now available only two:
33
+ # Also Hanami::Logger support different formatters. Now available only two:
34
34
  #
35
35
  # * Formatter (default)
36
36
  # * JSONFormatter
@@ -132,11 +132,11 @@ module Hanami
132
132
  # @since 0.8.0
133
133
  # @api private
134
134
  LEVELS = ::Hash[
135
- "debug" => DEBUG,
136
- "info" => INFO,
137
- "warn" => WARN,
138
- "error" => ERROR,
139
- "fatal" => FATAL,
135
+ "debug" => DEBUG,
136
+ "info" => INFO,
137
+ "warn" => WARN,
138
+ "error" => ERROR,
139
+ "fatal" => FATAL,
140
140
  "unknown" => UNKNOWN
141
141
  ].freeze
142
142
 
@@ -279,9 +279,9 @@ module Hanami
279
279
  # logger.info "Hello World"
280
280
  #
281
281
  # # => {"app":"Hanami","severity":"DEBUG","time":"2017-03-30T13:57:59Z","message":"Hello World"}
282
- # rubocop:disable Lint/SuppressedException
282
+ # rubocop:disable Lint/HandleExceptions
283
283
  # rubocop:disable Metrics/ParameterLists
284
- def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil) # rubocop:disable Layout/LineLength
284
+ def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil)
285
285
  begin
286
286
  Utils::Files.mkdir_p(stream)
287
287
  rescue TypeError
@@ -296,7 +296,7 @@ module Hanami
296
296
  end
297
297
 
298
298
  # rubocop:enable Metrics/ParameterLists
299
- # rubocop:enable Lint/SuppressedException
299
+ # rubocop:enable Lint/HandleExceptions
300
300
 
301
301
  # Returns the current application name, this is used for tagging purposes
302
302
  #
@@ -313,7 +313,7 @@ module Hanami
313
313
  super _level(value)
314
314
  end
315
315
 
316
- # Closes the logging stream if this stream isn't an STDOUT
316
+ # Close the logging stream if this stream isn't an STDOUT
317
317
  #
318
318
  # @since 0.8.0
319
319
  def close
@@ -14,9 +14,9 @@ module Hanami
14
14
  # @api private
15
15
  def call(app, severity, datetime, _progname)
16
16
  ::Hash[
17
- app: app,
17
+ app: app,
18
18
  severity: severity,
19
- time: datetime,
19
+ time: datetime,
20
20
  ]
21
21
  end
22
22
  end
@@ -44,9 +44,9 @@ module Hanami
44
44
  # @return [::Hash] an Hash containing the keys `:app`, `:severity`, and `:time`
45
45
  def call(app, severity, datetime, _progname)
46
46
  ::Hash[
47
- app: app(app),
47
+ app: app(app),
48
48
  severity: severity(severity),
49
- time: datetime(datetime),
49
+ time: datetime(datetime),
50
50
  ]
51
51
  end
52
52
 
@@ -57,18 +57,18 @@ module Hanami
57
57
  # @since 1.2.0
58
58
  # @api private
59
59
  COLORS = ::Hash[
60
- app: :blue,
60
+ app: :blue,
61
61
  datetime: :cyan,
62
62
  ].freeze
63
63
 
64
64
  # @since 1.2.0
65
65
  # @api private
66
66
  LEVELS = ::Hash[
67
- Hanami::Logger::DEBUG => :cyan,
68
- Hanami::Logger::INFO => :magenta,
69
- Hanami::Logger::WARN => :yellow,
70
- Hanami::Logger::ERROR => :red,
71
- Hanami::Logger::FATAL => :red,
67
+ Hanami::Logger::DEBUG => :cyan,
68
+ Hanami::Logger::INFO => :magenta,
69
+ Hanami::Logger::WARN => :yellow,
70
+ Hanami::Logger::ERROR => :red,
71
+ Hanami::Logger::FATAL => :red,
72
72
  Hanami::Logger::UNKNOWN => :blue,
73
73
  ].freeze
74
74
 
@@ -9,19 +9,21 @@ module Hanami
9
9
  # @since 1.1.0
10
10
  # @api private
11
11
  class Filter
12
- # @since 1.3.7
12
+ # @since 1.1.0
13
13
  # @api private
14
- FILTERED_VALUE = "[FILTERED]"
15
-
16
- def initialize(filters = [], mask: FILTERED_VALUE)
14
+ def initialize(filters = [])
17
15
  @filters = filters
18
- @mask = mask
19
16
  end
20
17
 
21
18
  # @since 1.1.0
22
19
  # @api private
23
- def call(params)
24
- _filter(_copy_params(params))
20
+ def call(hash)
21
+ _filtered_keys(hash).each do |key|
22
+ *keys, last = _actual_keys(hash, key.split("."))
23
+ keys.inject(hash, :fetch)[last] = "[FILTERED]"
24
+ end
25
+
26
+ hash
25
27
  end
26
28
 
27
29
  private
@@ -30,29 +32,6 @@ module Hanami
30
32
  # @api private
31
33
  attr_reader :filters
32
34
 
33
- # @since 1.3.7
34
- # @api private
35
- attr_reader :mask
36
-
37
- # This is a simple deep merge to merge the original input
38
- # with the filtered hash which contains '[FILTERED]' string.
39
- #
40
- # It only deep-merges if the conflict values are both hashes.
41
- #
42
- # @since 1.3.7
43
- # @api private
44
- def _deep_merge(original_hash, filtered_hash)
45
- original_hash.merge(filtered_hash) do |_key, original_item, filtered_item|
46
- if original_item.is_a?(Hash) && filtered_item.is_a?(Hash)
47
- _deep_merge(original_item, filtered_item)
48
- elsif filtered_item == FILTERED_VALUE
49
- filtered_item
50
- else
51
- original_item
52
- end
53
- end
54
- end
55
-
56
35
  # @since 1.1.0
57
36
  # @api private
58
37
  def _filtered_keys(hash)
@@ -63,7 +42,7 @@ module Hanami
63
42
  # @api private
64
43
  def _key_paths(hash, base = nil)
65
44
  hash.inject([]) do |results, (k, v)|
66
- results + (_key_paths?(v) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
45
+ results + (v.respond_to?(:each) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
67
46
  end
68
47
  end
69
48
 
@@ -84,64 +63,6 @@ module Hanami
84
63
  res + [correct_key]
85
64
  end
86
65
  end
87
-
88
- # Check if the given value can be iterated (`Enumerable`) and that isn't a `File`.
89
- # This is useful to detect closed `Tempfiles`.
90
- #
91
- # @since 1.3.5
92
- # @api private
93
- #
94
- # @see https://github.com/hanami/utils/pull/342
95
- def _key_paths?(value)
96
- value.is_a?(Enumerable) && !value.is_a?(File)
97
- end
98
-
99
- # @since 1.3.7
100
- # @api private
101
- def _deep_dup(hash)
102
- hash.map do |key, value|
103
- [
104
- key,
105
- if value.is_a?(Hash)
106
- _deep_dup(value)
107
- else
108
- _key_paths?(value) ? value.dup : value
109
- end
110
- ]
111
- end.to_h
112
- end
113
-
114
- # @since 1.3.7
115
- # @api private
116
- def _copy_params(params)
117
- case params
118
- when Hash
119
- _deep_dup(params)
120
- when Array
121
- params.map { |hash| _deep_dup(hash) }
122
- end
123
- end
124
-
125
- # @since 1.3.7
126
- # @api private
127
- def _filter_hash(hash)
128
- _filtered_keys(hash).each do |key|
129
- *keys, last = _actual_keys(hash, key.split("."))
130
- keys.inject(hash, :fetch)[last] = mask
131
- end
132
- hash
133
- end
134
-
135
- # @since 1.3.7
136
- # @api private
137
- def _filter(params)
138
- case params
139
- when Hash
140
- _filter_hash(params)
141
- when Array
142
- params.map { |hash| _filter_hash(hash) }
143
- end
144
- end
145
66
  end
146
67
  end
147
68
  end