smart_initializer 0.1.0.alpha4 → 0.1.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +36 -5
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile.lock +48 -31
  6. data/README.md +151 -22
  7. data/Rakefile +1 -1
  8. data/bin/rspec +54 -0
  9. data/gemfiles/with_external_deps.gemfile +7 -0
  10. data/gemfiles/with_external_deps.gemfile.lock +99 -0
  11. data/gemfiles/without_external_deps.gemfile +5 -0
  12. data/gemfiles/without_external_deps.gemfile.lock +97 -0
  13. data/lib/smart_core/initializer.rb +38 -35
  14. data/lib/smart_core/initializer/attribute.rb +12 -4
  15. data/lib/smart_core/initializer/attribute/factory.rb +47 -27
  16. data/lib/smart_core/initializer/attribute/parameters.rb +12 -4
  17. data/lib/smart_core/initializer/configurable_module.rb +27 -0
  18. data/lib/smart_core/initializer/configuration.rb +33 -0
  19. data/lib/smart_core/initializer/constructor.rb +9 -6
  20. data/lib/smart_core/initializer/constructor/definer.rb +34 -11
  21. data/lib/smart_core/initializer/dsl.rb +23 -6
  22. data/lib/smart_core/initializer/errors.rb +44 -0
  23. data/lib/smart_core/initializer/plugins.rb +17 -0
  24. data/lib/smart_core/initializer/plugins/abstract.rb +55 -0
  25. data/lib/smart_core/initializer/plugins/access_mixin.rb +47 -0
  26. data/lib/smart_core/initializer/plugins/registry.rb +166 -0
  27. data/lib/smart_core/initializer/plugins/registry_interface.rb +77 -0
  28. data/lib/smart_core/initializer/plugins/thy_types.rb +30 -0
  29. data/lib/smart_core/initializer/plugins/thy_types/errors.rb +11 -0
  30. data/lib/smart_core/initializer/plugins/thy_types/thy_types.rb +23 -0
  31. data/lib/smart_core/initializer/plugins/thy_types/thy_types/abstract_factory.rb +78 -0
  32. data/lib/smart_core/initializer/plugins/thy_types/thy_types/operation.rb +12 -0
  33. data/lib/smart_core/initializer/plugins/thy_types/thy_types/operation/base.rb +9 -0
  34. data/lib/smart_core/initializer/plugins/thy_types/thy_types/operation/cast.rb +21 -0
  35. data/lib/smart_core/initializer/plugins/thy_types/thy_types/operation/valid.rb +16 -0
  36. data/lib/smart_core/initializer/plugins/thy_types/thy_types/operation/validate.rb +19 -0
  37. data/lib/smart_core/initializer/settings.rb +49 -0
  38. data/lib/smart_core/initializer/settings/duplicator.rb +20 -0
  39. data/lib/smart_core/initializer/settings/type_system.rb +69 -0
  40. data/lib/smart_core/initializer/type_system.rb +16 -0
  41. data/lib/smart_core/initializer/type_system/interop.rb +103 -0
  42. data/lib/smart_core/initializer/type_system/interop/abstract_factory.rb +70 -0
  43. data/lib/smart_core/initializer/type_system/interop/aliasing.rb +72 -0
  44. data/lib/smart_core/initializer/type_system/interop/aliasing/alias_list.rb +141 -0
  45. data/lib/smart_core/initializer/type_system/interop/operation.rb +30 -0
  46. data/lib/smart_core/initializer/type_system/registry.rb +174 -0
  47. data/lib/smart_core/initializer/type_system/registry_interface.rb +102 -0
  48. data/lib/smart_core/initializer/type_system/smart_types.rb +48 -0
  49. data/lib/smart_core/initializer/type_system/smart_types/abstract_factory.rb +72 -0
  50. data/lib/smart_core/initializer/type_system/smart_types/operation.rb +13 -0
  51. data/lib/smart_core/initializer/type_system/smart_types/operation/base.rb +8 -0
  52. data/lib/smart_core/initializer/type_system/smart_types/operation/cast.rb +16 -0
  53. data/lib/smart_core/initializer/type_system/smart_types/operation/valid.rb +16 -0
  54. data/lib/smart_core/initializer/type_system/smart_types/operation/validate.rb +16 -0
  55. data/lib/smart_core/initializer/version.rb +1 -1
  56. data/smart_initializer.gemspec +13 -9
  57. metadata +67 -17
  58. data/lib/smart_core/initializer/attribute/init_extension.rb +0 -4
  59. data/lib/smart_core/initializer/type_aliasing.rb +0 -50
  60. data/lib/smart_core/initializer/type_aliasing/alias_list.rb +0 -101
@@ -43,12 +43,18 @@ class SmartCore::Initializer::Attribute::Parameters
43
43
  # @since 0.1.0
44
44
  attr_reader :name
45
45
 
46
- # @return [SmartCore::Types::Primitive]
46
+ # @return [SmartCore::Initializer::TypeSystem::Interop]
47
47
  #
48
48
  # @api private
49
49
  # @since 0.1.0
50
50
  attr_reader :type
51
51
 
52
+ # @return [Class<SmartCore::Initilizer::TypeSystem::Interop>]
53
+ #
54
+ # @api private
55
+ # @since 0.1.0
56
+ attr_reader :type_system
57
+
52
58
  # @return [Symbol]
53
59
  #
54
60
  # @api private
@@ -76,7 +82,8 @@ class SmartCore::Initializer::Attribute::Parameters
76
82
  attr_reader :dynamic_options
77
83
 
78
84
  # @param name [Symbol]
79
- # @param type [SmartCore::Types::Primitive]
85
+ # @param type [SmartCore::Initializer::TypeSystem::Interop]
86
+ # @param type_system [Class<SmartCore::Initializer::TypeSystem::Interop>]
80
87
  # @param privacy [Symbol]
81
88
  # @param finalizer [SmartCore::Initializer::Attribute::AnonymousBlock/InstanceMethod]
82
89
  # @param cast [Boolean]
@@ -86,9 +93,10 @@ class SmartCore::Initializer::Attribute::Parameters
86
93
  #
87
94
  # @api private
88
95
  # @since 0.1.0
89
- def initialize(name, type, privacy, finalizer, cast, dynamic_options)
96
+ def initialize(name, type, type_system, privacy, finalizer, cast, dynamic_options)
90
97
  @name = name
91
98
  @type = type
99
+ @type_system = type_system
92
100
  @privacy = privacy
93
101
  @finalizer = finalizer
94
102
  @cast = cast
@@ -116,6 +124,6 @@ class SmartCore::Initializer::Attribute::Parameters
116
124
  ERROR_MESSAGE
117
125
  end
118
126
 
119
- defalut_value.is_a?(Proc) ? default_value.call : default_value
127
+ default_value.is_a?(Proc) ? default_value.call : default_value
120
128
  end
121
129
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Initializer::ConfigurableModule
6
+ # @return [NilClass]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ INITIAL_TYPE_SYSTEM = nil
11
+
12
+ class << self
13
+ # @param type_system [String, Symbol, NilClass]
14
+ # @return [Module]
15
+ #
16
+ # @api private
17
+ # @since 0.1.0
18
+ def build(type_system: INITIAL_TYPE_SYSTEM)
19
+ Module.new.tap do |extension|
20
+ extension.singleton_class.define_method(:included) do |base_klass|
21
+ base_klass.include(::SmartCore::Initializer)
22
+ base_klass.__initializer_settings__.type_system = type_system
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'qonfig'
4
+
5
+ # @api public
6
+ # @since 0.1.0
7
+ module SmartCore::Initializer::Configuration
8
+ # @since 0.1.0
9
+ include Qonfig::Configurable
10
+
11
+ # @api public
12
+ # @since 0.1.0
13
+ extend SmartCore::Initializer::Plugins::AccessMixin
14
+
15
+ class << self
16
+ # @param setting_key [String, Symbol]
17
+ # @return [Qonfig::Settings, Any]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def [](setting_key)
22
+ config[setting_key]
23
+ end
24
+ end
25
+
26
+ # @since 0.1.0
27
+ configuration do
28
+ setting :default_type_system, :smart_types
29
+ validate :default_type_system do |value|
30
+ SmartCore::Initializer::TypeSystem.resolve(value) rescue false
31
+ end
32
+ end
33
+ end
@@ -107,10 +107,10 @@ class SmartCore::Initializer::Constructor
107
107
  parameter_definitions = Hash[klass.__params__.zip(parameters)]
108
108
 
109
109
  parameter_definitions.each_pair do |attribute, parameter_value|
110
- parameter_value = attribute.type.cast(parameter_value) if attribute.cast?
110
+ if !attribute.type.valid?(parameter_value) && attribute.cast?
111
+ parameter_value = attribute.type.cast(parameter_value)
112
+ end
111
113
 
112
- # TODO: fail with SmartCore::Initializer::ArgumentError
113
- # (isntead of SmartCore::Types::TypeError)
114
114
  attribute.type.validate!(parameter_value)
115
115
 
116
116
  final_value = attribute.finalizer.call(parameter_value, instance)
@@ -123,19 +123,22 @@ class SmartCore::Initializer::Constructor
123
123
  #
124
124
  # @api private
125
125
  # @since 0.1.0
126
+ # rubocop:disable Metrics/AbcSize
126
127
  def initialize_options(instance)
127
128
  klass.__options__.each do |attribute|
128
129
  option_value = options.fetch(attribute.name) { attribute.default }
129
- option_value = attribute.type.cast(option_value) if attribute.cast?
130
130
 
131
- # TODO: fail with SmartCore::Initializer::ArgumentError
132
- # (isntead of SmartCore::Types::TypeError)
131
+ if !attribute.type.valid?(option_value) && attribute.cast?
132
+ option_value = attribute.type.cast(option_value)
133
+ end
134
+
133
135
  attribute.type.validate!(option_value)
134
136
 
135
137
  final_value = attribute.finalizer.call(option_value, instance)
136
138
  instance.instance_variable_set("@#{attribute.name}", final_value)
137
139
  end
138
140
  end
141
+ # rubocop:enable Metrics/AbcSize
139
142
 
140
143
  # @param instance [Any]
141
144
  # @return [void]
@@ -2,6 +2,7 @@
2
2
 
3
3
  # @api private
4
4
  # @since 0.1.0
5
+ # rubocop:disable Metrics/ClassLength
5
6
  class SmartCore::Initializer::Constructor::Definer
6
7
  # @param klass [Class]
7
8
  # @return [void]
@@ -25,7 +26,8 @@ class SmartCore::Initializer::Constructor::Definer
25
26
  end
26
27
 
27
28
  # @param name [String, Symbol]
28
- # @param type [String, Symbol, SmartCore::Types::Primitive]
29
+ # @param type [String, Symbol, Any]
30
+ # @param type_system [String, Symbol]
29
31
  # @param privacy [String, Symbol]
30
32
  # @param finalize [String, Symbol, Proc]
31
33
  # @param cast [Boolean]
@@ -34,9 +36,17 @@ class SmartCore::Initializer::Constructor::Definer
34
36
  #
35
37
  # @api private
36
38
  # @since 0.1.0
37
- def define_parameter(name, type, privacy, finalize, cast, dynamic_options)
39
+ def define_parameter(name, type, type_system, privacy, finalize, cast, dynamic_options)
38
40
  thread_safe do
39
- attribute = build_attribute(name, type, privacy, finalize, cast, dynamic_options)
41
+ attribute = build_attribute(
42
+ name,
43
+ type,
44
+ type_system,
45
+ privacy,
46
+ finalize,
47
+ cast,
48
+ dynamic_options
49
+ )
40
50
  prevent_option_overlap(attribute)
41
51
  add_parameter(attribute)
42
52
  end
@@ -52,7 +62,8 @@ class SmartCore::Initializer::Constructor::Definer
52
62
  names.map do |name|
53
63
  build_attribute(
54
64
  name,
55
- SmartCore::Types::Value::Any,
65
+ klass.__initializer_settings__.generic_type_object,
66
+ klass.__initializer_settings__.type_system,
56
67
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_PRIVACY_MODE,
57
68
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_FINALIZER,
58
69
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_CAST_BEHAVIOUR,
@@ -67,7 +78,8 @@ class SmartCore::Initializer::Constructor::Definer
67
78
  end
68
79
 
69
80
  # @param name [String, Symbol]
70
- # @param type [String, Symbol, SmartCore::Types::Primitive]
81
+ # @param type [String, Symbol, Any]
82
+ # @param type_system [String, Symbol]
71
83
  # @param privacy [String, Symbol]
72
84
  # @param finalize [String, Symbol, Proc]
73
85
  # @param cast [Boolean]
@@ -76,9 +88,17 @@ class SmartCore::Initializer::Constructor::Definer
76
88
  #
77
89
  # @api private
78
90
  # @since 0.1.0
79
- def define_option(name, type, privacy, finalize, cast, dynamic_options)
91
+ def define_option(name, type, type_system, privacy, finalize, cast, dynamic_options)
80
92
  thread_safe do
81
- attribute = build_attribute(name, type, privacy, finalize, cast, dynamic_options)
93
+ attribute = build_attribute(
94
+ name,
95
+ type,
96
+ type_system,
97
+ privacy,
98
+ finalize,
99
+ cast,
100
+ dynamic_options
101
+ )
82
102
  prevent_parameter_overlap(attribute)
83
103
  add_option(attribute)
84
104
  end
@@ -94,7 +114,8 @@ class SmartCore::Initializer::Constructor::Definer
94
114
  names.map do |name|
95
115
  build_attribute(
96
116
  name,
97
- SmartCore::Types::Value::Any,
117
+ klass.__initializer_settings__.generic_type_object,
118
+ klass.__initializer_settings__.type_system,
98
119
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_PRIVACY_MODE,
99
120
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_FINALIZER,
100
121
  SmartCore::Initializer::Attribute::Parameters::DEFAULT_CAST_BEHAVIOUR,
@@ -117,7 +138,8 @@ class SmartCore::Initializer::Constructor::Definer
117
138
  attr_reader :klass
118
139
 
119
140
  # @param name [String, Symbol]
120
- # @param type [String, Symbol, SmartCore::Types::Primitive]
141
+ # @param type [String, Symbol, Any]
142
+ # @param type_system [String, Symbol]
121
143
  # @param privacy [String, Symbol]
122
144
  # @param finalize [String, Symbol, Proc]
123
145
  # @param cast [Boolean]
@@ -126,9 +148,9 @@ class SmartCore::Initializer::Constructor::Definer
126
148
  #
127
149
  # @api private
128
150
  # @since 0.1.0
129
- def build_attribute(name, type, privacy, finalize, cast, dynamic_options)
151
+ def build_attribute(name, type, type_system, privacy, finalize, cast, dynamic_options)
130
152
  SmartCore::Initializer::Attribute::Factory.create(
131
- name, type, privacy, finalize, cast, dynamic_options
153
+ name, type, type_system, privacy, finalize, cast, dynamic_options
132
154
  )
133
155
  end
134
156
 
@@ -207,3 +229,4 @@ class SmartCore::Initializer::Constructor::Definer
207
229
  @lock.synchronize(&block)
208
230
  end
209
231
  end
232
+ # rubocop:enable Metrics/ClassLength
@@ -18,6 +18,7 @@ module SmartCore::Initializer::DSL
18
18
  instance_variable_set(:@__init_extensions__, SmartCore::Initializer::Extensions::List.new)
19
19
  instance_variable_set(:@__definer__, SmartCore::Initializer::Constructor::Definer.new(self))
20
20
  instance_variable_set(:@__deflock__, SmartCore::Engine::Lock.new)
21
+ instance_variable_set(:@__initializer_settings__, SmartCore::Initializer::Settings.new)
21
22
  end
22
23
  base_klass.extend(ClassMethods)
23
24
  base_klass.singleton_class.prepend(ClassInheritance)
@@ -39,6 +40,7 @@ module SmartCore::Initializer::DSL
39
40
  instance_variable_set(:@__init_extensions__, SmartCore::Initializer::Extensions::List.new)
40
41
  instance_variable_set(:@__definer__, SmartCore::Initializer::Constructor::Definer.new(self))
41
42
  instance_variable_set(:@__deflock__, SmartCore::Engine::Lock.new)
43
+ instance_variable_set(:@__initializer_settings__, __initializer_settings__.dup)
42
44
  end
43
45
  child_klass.extend(ClassMethods)
44
46
  SmartCore::Initializer::DSL::Inheritance.inherit(base: self, child: child_klass)
@@ -81,6 +83,14 @@ module SmartCore::Initializer::DSL
81
83
  @__definer__
82
84
  end
83
85
 
86
+ # @return [SmartCore::Initializer::Settings]
87
+ #
88
+ # @api private
89
+ # @since 0.1.0
90
+ def __initializer_settings__
91
+ @__initializer_settings__
92
+ end
93
+
84
94
  # @param arguments [Array]
85
95
  # @param block [Block]
86
96
  # @return [Any]
@@ -92,7 +102,7 @@ module SmartCore::Initializer::DSL
92
102
  end
93
103
 
94
104
  # @param name [String, Symbol]
95
- # @param type [String, Symbol, SmartCore::Types::Primitive]
105
+ # @param type [String, Symbol, Any]
96
106
  # @option cast [Boolean]
97
107
  # @option privacy [String, Symbol]
98
108
  # @option finalize [String, Symbol, Proc]
@@ -103,13 +113,16 @@ module SmartCore::Initializer::DSL
103
113
  # @since 0.1.0
104
114
  def param(
105
115
  name,
106
- type = SmartCore::Types::Value::Any,
116
+ type = __initializer_settings__.generic_type_object,
107
117
  privacy: SmartCore::Initializer::Attribute::Parameters::DEFAULT_PRIVACY_MODE,
108
118
  finalize: SmartCore::Initializer::Attribute::Parameters::DEFAULT_FINALIZER,
109
119
  cast: SmartCore::Initializer::Attribute::Parameters::DEFAULT_CAST_BEHAVIOUR,
120
+ type_system: __initializer_settings__.type_system,
110
121
  **dynamic_options
111
122
  )
112
- __definer__.define_parameter(name, type, privacy, finalize, cast, dynamic_options)
123
+ __definer__.define_parameter(
124
+ name, type, type_system, privacy, finalize, cast, dynamic_options
125
+ )
113
126
  end
114
127
 
115
128
  # @param names [Array<String, Symbol>]
@@ -122,10 +135,11 @@ module SmartCore::Initializer::DSL
122
135
  end
123
136
 
124
137
  # @param name [String, Symbol]
125
- # @param type [String, Symbol, SmartCore::Types::Primitive]
138
+ # @param type [String, Symbol, Any]
126
139
  # @option cast [Boolean]
127
140
  # @option privacy [String, Symbol]
128
141
  # @option finalize [String, Symbol, Proc]
142
+ # @option type_system [String, Symbol]
129
143
  # @param dynamic_options [Hash<Symbol,Any>]
130
144
  # @return [void]
131
145
  #
@@ -133,13 +147,16 @@ module SmartCore::Initializer::DSL
133
147
  # @since 0.1.0
134
148
  def option(
135
149
  name,
136
- type = SmartCore::Types::Value::Any,
150
+ type = __initializer_settings__.generic_type_object,
137
151
  privacy: SmartCore::Initializer::Attribute::Parameters::DEFAULT_PRIVACY_MODE,
138
152
  finalize: SmartCore::Initializer::Attribute::Parameters::DEFAULT_FINALIZER,
139
153
  cast: SmartCore::Initializer::Attribute::Parameters::DEFAULT_CAST_BEHAVIOUR,
154
+ type_system: __initializer_settings__.type_system,
140
155
  **dynamic_options
141
156
  )
142
- __definer__.define_option(name, type, privacy, finalize, cast, dynamic_options)
157
+ __definer__.define_option(
158
+ name, type, type_system, privacy, finalize, cast, dynamic_options
159
+ )
143
160
  end
144
161
 
145
162
  # @param names [Array<String, Symbol>]
@@ -32,4 +32,48 @@ module SmartCore::Initializer
32
32
  # @api public
33
33
  # @since 0.1.0
34
34
  NoTypeAliasError = Class.new(Error)
35
+
36
+ # @api public
37
+ # @since 0.1.0
38
+ PluginError = Class.new(Error)
39
+
40
+ # @api public
41
+ # @since 0.1.0
42
+ UnresolvedPluginDependencyError = Class.new(PluginError)
43
+
44
+ # @api public
45
+ # @since 0.1.0
46
+ AlreadyRegisteredPluginError = Class.new(PluginError)
47
+
48
+ # @api public
49
+ # @since 0.1.0
50
+ UnregisteredPluginError = Class.new(PluginError)
51
+
52
+ # @api public
53
+ # @since 0.1.0
54
+ TypeSystemError = Class.new(Error)
55
+
56
+ # @api public
57
+ # @since 0.1.0
58
+ TypeAliasNotFoundError = Class.new(TypeSystemError)
59
+
60
+ # @api public
61
+ # @since 0.1.0
62
+ IncorrectTypeSystemInteropError = Class.new(TypeSystemError)
63
+
64
+ # @api public
65
+ # @since 0.1.0
66
+ IncorrectTypeObjectError = Class.new(TypeSystemError)
67
+
68
+ # @api public
69
+ # @since 0.1.0
70
+ UnsupportedTypeSystemError = Class.new(TypeSystemError)
71
+
72
+ # @api public
73
+ # @since 0.1.0
74
+ UnsupportedTypeOperationError = Class.new(TypeSystemError)
75
+
76
+ # @api public
77
+ # @since 0.1.0
78
+ TypeCastingUnsupportedError = Class.new(UnsupportedTypeOperationError)
35
79
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Initializer::Plugins
6
+ require_relative 'plugins/abstract'
7
+ require_relative 'plugins/registry'
8
+ require_relative 'plugins/registry_interface.rb'
9
+ require_relative 'plugins/access_mixin'
10
+ require_relative 'plugins/thy_types'
11
+
12
+ # @since 0.1.0
13
+ extend SmartCore::Initializer::Plugins::RegistryInterface
14
+
15
+ # @since 0.1.0
16
+ register_plugin('thy_types', SmartCore::Initializer::Plugins::ThyTypes)
17
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Initializer::Plugins::Abstract
6
+ class << self
7
+ # @param child_klass [Class]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def inherited(child_klass)
13
+ child_klass.instance_variable_set(:@__loaded__, false)
14
+ child_klass.instance_variable_set(:@__lock__, Mutex.new)
15
+ super
16
+ end
17
+
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.1.0
22
+ def load!
23
+ __thread_safe__ do
24
+ unless @__loaded__
25
+ @__loaded__ = true
26
+ install!
27
+ end
28
+ end
29
+ end
30
+
31
+ # @return [Boolean]
32
+ #
33
+ # @api private
34
+ # @since 0.1.0
35
+ def loaded?
36
+ __thread_safe__ { @__loaded__ }
37
+ end
38
+
39
+ private
40
+
41
+ # @return [void]
42
+ #
43
+ # @api private
44
+ # @since 0.1.0
45
+ def install!; end
46
+
47
+ # @return [Any]
48
+ #
49
+ # @api private
50
+ # @since 0.1.0
51
+ def __thread_safe__
52
+ @__lock__.synchronize { yield }
53
+ end
54
+ end
55
+ end