patir 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35fea6bd6b6bda61f2d2eed6dfb2df5bdb429446
4
+ data.tar.gz: 440708159e9b1a9fb0736d849dd55eda61f85335
5
+ SHA512:
6
+ metadata.gz: faef027ff230d72c5ba4f1d379fc6c2a9954550539a468be9b739bf33465ffdea5441870972179b4a370f0e7d2a10b1f6e0020016fa9dcf55176d752873768f7
7
+ data.tar.gz: 1a0c00d712cfb9b142c80b8e299ade0b23c15efd21679d897ea606c25c2b16f8bfdc01e8e1afc87bc3035f8833d647110f7fa80fe0f694c4d4a44589236c61ea
@@ -1,77 +1,81 @@
1
- ==0.8.1 / 2012-11-28
2
- * be a good citizen and do not require rubygems anywhere
3
- ==0.8.0 / 2012-11-28
4
- * Patir::ShellCommand now has a timeout option to terminate a command after a timeout passes.
5
- * Removed Ruby warnings
6
- * Updated gem dependencies
7
-
8
- ==0.7.3 / 2012-07-25
9
- * Maintenance release
10
- * Updated gem dependencies
11
- * Moved to separate repository
12
- * Updated documenation
13
- ==0.7.2 / 2011-11-29
14
- * you can now use require in configuration files
15
- * better handling of paths in configuration files. Relative paths in chained configuration files are now correct
16
- * more unit tests
17
- ==0.7.0 / 2011-08-11
18
- * Ruby 1.9 compatibility
19
- * updated systemu dependency
20
- * Unit tests extended
21
- * added context parameter to RubyCommand instances
22
- * ShellCommand now sets the error output consistently (at times it was nil)
23
-
24
- ==0.6.4 / 2009-09-04
25
- * context parameter added to Command#run
26
- ==0.6.3
27
- * Repository restructured (git is used internally so the svn repository matches now the master branch)
28
- * Cleanup. Some obsolete code was removed
29
- * Unit tests clarified
30
- ==0.6.2
31
- * RubyCommand and ShellCommand are now more robust handling StandardError exceptions.
32
- ==0.6.1
33
- * RubyCommand failure logs are now clearer no more 'RubyCommand failed:' redundancies.
34
- * RubyCommand: Backtrace added to error output if $DEBUG=true
35
- ==0.6.0
36
- * RubyCommand now sets success when the block runs to the end. Block returns values are ignored. To indicate failure raise an exception.
37
-
38
- == 0.5.8 /2008-09-18
39
- * CommandSequence status updates set the correct status
40
- == 0.5.7 / 2008-09-10
41
- * Sorted CommandSequenceStatus summaries
42
- == 0.5.6 / 2008-05-16
43
- * Better exception handling for RubyCommand.
44
- * Error output of command now contains exception information in case of one.
45
- * jRuby combatibility fixes in Command.
46
-
47
- == 0.5.5 / 2008-03-13
48
- * RubyCommand does not expand working directories anymore and it now exposes working_directory and the assigned block (as cmd)
49
- == 0.5.4 / 2008-03-13
50
- * ShellCommand does not expand working directories anymore
51
- * Configurator#logger method redefinition removed
52
- == 0.5.3 / 2008-02-26
53
- * Bugfix: execution time is now correctly calculated (no nasty exception)
54
- == 0.5.2 / 2008-02-26
55
- * One should let the unit tests run before releasing!
56
-
57
- == 0.5.1 / 2008-02-26
58
- * A couple more unit tests
59
- * bugfix: command working directories are now fully expanded to avoid invalid nested chdir calls
60
- == 0.5.0 / 2007-07-13
61
- * setup_logger now accepts both Logger levels and :mute, :silent, :debug
62
- * 3 part version number. Version module with MAJOR, MINOR and TINY
63
- == 0.4.0 / 2007-05-30
64
- * Configurator#load_from_file method added to allow for configuration file inclusions
65
-
66
- == 0.3.0 / 2007-05-20
67
- * Bugfix for the nesting of CommandSequenceStatus
68
- * PatirLoggerFormatter added and used in setup_logger
69
-
70
- == 0.2.0 / 2007-03-22
71
- * Added the Command library
72
- * Implementations: ShellCommand, RubyCommand
73
-
74
- == 0.1.0 / 2007-03-20
75
- * First version
76
- * Base library with logger setup and drb setup methods
77
- * Configuration library
1
+ ==0.8.2 / 2015-07-10
2
+ * Fix an ancient RubyCommand bug that did not set error when an exception was raised in the block
3
+ * Removed deprecation warnings
4
+
5
+ ==0.8.1 / 2012-11-28
6
+ * be a good citizen and do not require rubygems anywhere
7
+ ==0.8.0 / 2012-11-28
8
+ * Patir::ShellCommand now has a timeout option to terminate a command after a timeout passes.
9
+ * Removed Ruby warnings
10
+ * Updated gem dependencies
11
+
12
+ ==0.7.3 / 2012-07-25
13
+ * Maintenance release
14
+ * Updated gem dependencies
15
+ * Moved to separate repository
16
+ * Updated documenation
17
+ ==0.7.2 / 2011-11-29
18
+ * you can now use require in configuration files
19
+ * better handling of paths in configuration files. Relative paths in chained configuration files are now correct
20
+ * more unit tests
21
+ ==0.7.0 / 2011-08-11
22
+ * Ruby 1.9 compatibility
23
+ * updated systemu dependency
24
+ * Unit tests extended
25
+ * added context parameter to RubyCommand instances
26
+ * ShellCommand now sets the error output consistently (at times it was nil)
27
+
28
+ ==0.6.4 / 2009-09-04
29
+ * context parameter added to Command#run
30
+ ==0.6.3
31
+ * Repository restructured (git is used internally so the svn repository matches now the master branch)
32
+ * Cleanup. Some obsolete code was removed
33
+ * Unit tests clarified
34
+ ==0.6.2
35
+ * RubyCommand and ShellCommand are now more robust handling StandardError exceptions.
36
+ ==0.6.1
37
+ * RubyCommand failure logs are now clearer no more 'RubyCommand failed:' redundancies.
38
+ * RubyCommand: Backtrace added to error output if $DEBUG=true
39
+ ==0.6.0
40
+ * RubyCommand now sets success when the block runs to the end. Block returns values are ignored. To indicate failure raise an exception.
41
+
42
+ == 0.5.8 /2008-09-18
43
+ * CommandSequence status updates set the correct status
44
+ == 0.5.7 / 2008-09-10
45
+ * Sorted CommandSequenceStatus summaries
46
+ == 0.5.6 / 2008-05-16
47
+ * Better exception handling for RubyCommand.
48
+ * Error output of command now contains exception information in case of one.
49
+ * jRuby combatibility fixes in Command.
50
+
51
+ == 0.5.5 / 2008-03-13
52
+ * RubyCommand does not expand working directories anymore and it now exposes working_directory and the assigned block (as cmd)
53
+ == 0.5.4 / 2008-03-13
54
+ * ShellCommand does not expand working directories anymore
55
+ * Configurator#logger method redefinition removed
56
+ == 0.5.3 / 2008-02-26
57
+ * Bugfix: execution time is now correctly calculated (no nasty exception)
58
+ == 0.5.2 / 2008-02-26
59
+ * One should let the unit tests run before releasing!
60
+
61
+ == 0.5.1 / 2008-02-26
62
+ * A couple more unit tests
63
+ * bugfix: command working directories are now fully expanded to avoid invalid nested chdir calls
64
+ == 0.5.0 / 2007-07-13
65
+ * setup_logger now accepts both Logger levels and :mute, :silent, :debug
66
+ * 3 part version number. Version module with MAJOR, MINOR and TINY
67
+ == 0.4.0 / 2007-05-30
68
+ * Configurator#load_from_file method added to allow for configuration file inclusions
69
+
70
+ == 0.3.0 / 2007-05-20
71
+ * Bugfix for the nesting of CommandSequenceStatus
72
+ * PatirLoggerFormatter added and used in setup_logger
73
+
74
+ == 0.2.0 / 2007-03-22
75
+ * Added the Command library
76
+ * Implementations: ShellCommand, RubyCommand
77
+
78
+ == 0.1.0 / 2007-03-20
79
+ * First version
80
+ * Base library with logger setup and drb setup methods
81
+ * Configuration library
@@ -1,16 +1,16 @@
1
- History.txt
2
- Manifest.txt
3
- README.txt
4
- Rakefile
5
- lib/patir/base.rb
6
- lib/patir/command.rb
7
- lib/patir/configuration.rb
8
- test/samples/chain.cfg
9
- test/samples/config_fail.cfg
10
- test/samples/empty.cfg
11
- test/samples/failed.cfg
12
- test/samples/failed_unknown.cfg
13
- test/samples/syntax.cfg
14
- test/test_patir_base.rb
15
- test/test_patir_command.rb
16
- test/test_patir_configuration.rb
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/patir/base.rb
6
+ lib/patir/command.rb
7
+ lib/patir/configuration.rb
8
+ test/samples/chain.cfg
9
+ test/samples/config_fail.cfg
10
+ test/samples/empty.cfg
11
+ test/samples/failed.cfg
12
+ test/samples/failed_unknown.cfg
13
+ test/samples/syntax.cfg
14
+ test/test_patir_base.rb
15
+ test/test_patir_command.rb
16
+ test/test_patir_configuration.rb
data/README.txt CHANGED
@@ -1,38 +1,38 @@
1
- patir http://patir.rubyforge.org
2
- == DESCRIPTION:
3
-
4
- patir provides code to enable project automation tasks:
5
-
6
- * A logging format for ruby's built-in Logger
7
- * A command abstraction with a platform independent implementation for running shell commands and ruby code
8
- * Command sequences using the same command abstraction as single commands.
9
- * Configuration format for configuration files written in ruby.
10
-
11
- == INSTALL:
12
-
13
- gem install patir
14
-
15
- == LICENSE:
16
-
17
- The MIT License)
18
-
19
- Copyright (c) 2007-2012 Vassilis Rizopoulos
20
-
21
- Permission is hereby granted, free of charge, to any person obtaining
22
- a copy of this software and associated documentation files (the
23
- 'Software'), to deal in the Software without restriction, including
24
- without limitation the rights to use, copy, modify, merge, publish,
25
- distribute, sublicense, and/or sell copies of the Software, and to
26
- permit persons to whom the Software is furnished to do so, subject to
27
- the following conditions:
28
-
29
- The above copyright notice and this permission notice shall be
30
- included in all copies or substantial portions of the Software.
31
-
32
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
33
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
35
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
36
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
37
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1
+ patir http://patir.rubyforge.org
2
+ == DESCRIPTION:
3
+
4
+ patir provides code to enable project automation tasks:
5
+
6
+ * A logging format for ruby's built-in Logger
7
+ * A command abstraction with a platform independent implementation for running shell commands and ruby code
8
+ * Command sequences using the same command abstraction as single commands.
9
+ * Configuration format for configuration files written in ruby.
10
+
11
+ == INSTALL:
12
+
13
+ gem install patir
14
+
15
+ == LICENSE:
16
+
17
+ The MIT License)
18
+
19
+ Copyright (c) 2007-2012 Vassilis Rizopoulos
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining
22
+ a copy of this software and associated documentation files (the
23
+ 'Software'), to deal in the Software without restriction, including
24
+ without limitation the rights to use, copy, modify, merge, publish,
25
+ distribute, sublicense, and/or sell copies of the Software, and to
26
+ permit persons to whom the Software is furnished to do so, subject to
27
+ the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be
30
+ included in all copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
33
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
35
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
36
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
37
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
38
38
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,20 +1,18 @@
1
- # -*- ruby -*-
2
- $:.unshift File.join(File.dirname(__FILE__),"lib")
3
- require 'hoe'
4
- require 'patir/base'
5
-
6
- Hoe.plugins.delete :compiler
7
-
8
- Hoe.spec('patir') do
9
- self.version = Patir::Version::STRING
10
- self.rubyforge_name = 'patir'
11
- self.author = "Vassilis Rizopoulos"
12
- self.email = "vassilisrizopoulos@gmail.com"
13
- self.summary = 'patir (Project Automation Tools in Ruby) provides libraries for use in project automation tools'
14
- self.description = paragraphs_of('README.md', 1..4).join("\n\n")
15
- self.urls = ["http://patir.rubyforge.org","http://github.com/damphyr/patir"]
16
- self.changes = paragraphs_of('History.txt', 0..1).join("\n\n")
17
- extra_deps<<['systemu',"~>2.5"]
18
- end
19
-
20
- # vim: syntax=Ruby
1
+ # -*- ruby -*-
2
+ $:.unshift File.join(File.dirname(__FILE__),"lib")
3
+ require 'hoe'
4
+ require 'patir/base'
5
+
6
+ Hoe.spec('patir') do |prj|
7
+ developer("Vassilis Rizopoulos", "vassilisrizopoulos@gmail.com")
8
+ license "MIT"
9
+ prj.version = Patir::Version::STRING
10
+ prj.summary='patir (Project Automation Tools in Ruby) provides libraries for use in project automation tools'
11
+ prj.urls=["http://github.com/damphyr/patir"]
12
+ prj.description=prj.paragraphs_of('README.md',1..4).join("\n\n")
13
+ prj.local_rdoc_dir='doc/rdoc'
14
+ prj.readme_file="README.md"
15
+ prj.extra_deps<<["systemu", "~>2.6"]
16
+ end
17
+
18
+ # vim: syntax=Ruby
@@ -6,7 +6,7 @@ module Patir
6
6
  module Version
7
7
  MAJOR=0
8
8
  MINOR=8
9
- TINY=1
9
+ TINY=2
10
10
  STRING=[ MAJOR, MINOR, TINY ].join( "." )
11
11
  end
12
12
  #Error thrown usually in initialize methods when missing required parameters
@@ -115,6 +115,8 @@ module Patir
115
115
  @command=params[:cmd]
116
116
  @status=:not_executed
117
117
  @timeout=params[:timeout]
118
+ @error=""
119
+ @output=""
118
120
  end
119
121
 
120
122
  #Executes the shell command and returns the status
@@ -125,7 +127,6 @@ module Patir
125
127
  FileUtils::mkdir_p(@working_directory,:verbose=>false)
126
128
  #create the actual command, run it, grab stderr and stdout and set output,error, status and execution time
127
129
  if @timeout
128
- @error=""
129
130
  exited=nil
130
131
  exitstatus=0
131
132
  status, @output, err = systemu(@command,:cwd=>@working_directory) do |cid|
@@ -158,7 +159,6 @@ module Patir
158
159
  end
159
160
  rescue
160
161
  #if it blows in systemu it will be nil
161
- @error||=""
162
162
  @error<<"\n#{$!.message}"
163
163
  @error<<"\n#{$!.backtrace}" if $DEBUG
164
164
  @status=:error
@@ -492,6 +492,8 @@ module Patir
492
492
  def run context=nil
493
493
  @run=true
494
494
  @context=context
495
+ @error=""
496
+ @output=""
495
497
  begin
496
498
  t1=Time.now
497
499
  Dir.chdir(@working_directory) do
@@ -499,8 +501,8 @@ module Patir
499
501
  @status=:success
500
502
  end
501
503
  rescue StandardError
502
- error<<"\n#{$!.message}"
503
- error<<"\n#{$!.backtrace}" if $DEBUG
504
+ @error<<"\n#{$!.message}"
505
+ @error<<"\n#{$!.backtrace}" if $DEBUG
504
506
  @status=:error
505
507
  ensure
506
508
  @exec_time=Time.now-t1
@@ -1,93 +1,93 @@
1
- # Copyright (c) 2007-2012 Vassilis Rizopoulos. All rights reserved.
2
-
3
- require 'patir/base'
4
- module Patir
5
- #This exception is thrown when encountering a configuration error
6
- class ConfigurationException<RuntimeError
7
- end
8
-
9
- #Configurator is the base class for all the Patir configuration classes.
10
- #
11
- #The idea behind the configurator is that the developer creates a module that contains as methods
12
- #all the configuration directives.
13
- #He then derives a class from Configurator and includes the directives module.
14
- #The Configurator loads the configuration file and evals it with itself as context (variable configuration), so the directives become methods in the configuration file:
15
- # configuration.directive="some value"
16
- # configuration.other_directive={:key=>"way to group values together",:other_key=>"omg"}
17
- #
18
- #The Configurator instance contains all the configuration data.
19
- #Configurator#configuration method is provided as a post-processing step. It should be overriden to return the configuration data in the desired format and perform any overall validation steps (single element validation steps should be done in the directives module).
20
- #==Example
21
- # module SimpleConfiguration
22
- # def name= tool_name
23
- # raise Patir::ConfigurationException,"Inappropriate language not allowed" if tool_name=="@#!&@&$}"
24
- # @name=tool_name
25
- # end
26
- # end
27
- #
28
- # class SimpleConfigurator
29
- # include SimpleConfiguration
30
- #
31
- # def configuration
32
- # return @name
33
- # end
34
- # end
35
- #The configuration file would then be
36
- # configuration.name="really polite name"
37
- #To use it you would do
38
- # cfg=SimpleConfigurator.new("config.cfg").configuration
39
- class Configurator
40
- attr_reader :logger,:config_file,:wd
41
- def initialize config_file,logger=nil
42
- @logger=logger
43
- @logger||=Patir.setup_logger
44
- @config_file=config_file
45
- load_configuration(@config_file)
46
- end
47
-
48
- #Returns self. This should be overriden in the actual implementations
49
- def configuration
50
- return self
51
- end
52
-
53
- #Loads the configuration from a file
54
- #
55
- #Use this to chain configuration files together
56
- #==Example
57
- #Say you have on configuration file "first.cfg" that contains all the generic directives and several others that change only one or two things.
58
- #
59
- #You can 'include' the first.cfg file in the other configurations with
60
- # configuration.load_from_file("first.cfg")
61
- def load_from_file filename
62
- fnm = File.exists?(filename) ? filename : File.join(@wd,filename)
63
- load_configuration(fnm)
64
- end
65
- private
66
- def load_configuration filename
67
- begin
68
- cfg_txt=File.read(filename)
69
- @wd=File.expand_path(File.dirname(filename))
70
- configuration=self
71
- #add the path to the require lookup path to allow require statements in the configuration files
72
- $:.unshift File.join(@wd)
73
- #evaluate in the working directory to enable relative paths in configuration
74
- Dir.chdir(@wd){eval(cfg_txt,binding())}
75
- @logger.info("Configuration loaded from #{filename}") if @logger
76
- rescue ConfigurationException
77
- #pass it on, do not wrap again
78
- raise
79
- rescue SyntaxError
80
- #Just wrap the exception so we can differentiate
81
- @logger.debug($!)
82
- raise ConfigurationException.new,"Syntax error in the configuration file '#{filename}':\n#{$!.message}"
83
- rescue NoMethodError
84
- @logger.debug($!)
85
- raise ConfigurationException.new,"Encountered an unknown directive in configuration file '#{filename}':\n#{$!.message}"
86
- rescue
87
- @logger.debug($!)
88
- #Just wrap the exception so we can differentiate
89
- raise ConfigurationException.new,"#{$!.message}"
90
- end
91
- end
92
- end
1
+ # Copyright (c) 2007-2012 Vassilis Rizopoulos. All rights reserved.
2
+
3
+ require 'patir/base'
4
+ module Patir
5
+ #This exception is thrown when encountering a configuration error
6
+ class ConfigurationException<RuntimeError
7
+ end
8
+
9
+ #Configurator is the base class for all the Patir configuration classes.
10
+ #
11
+ #The idea behind the configurator is that the developer creates a module that contains as methods
12
+ #all the configuration directives.
13
+ #He then derives a class from Configurator and includes the directives module.
14
+ #The Configurator loads the configuration file and evals it with itself as context (variable configuration), so the directives become methods in the configuration file:
15
+ # configuration.directive="some value"
16
+ # configuration.other_directive={:key=>"way to group values together",:other_key=>"omg"}
17
+ #
18
+ #The Configurator instance contains all the configuration data.
19
+ #Configurator#configuration method is provided as a post-processing step. It should be overriden to return the configuration data in the desired format and perform any overall validation steps (single element validation steps should be done in the directives module).
20
+ #==Example
21
+ # module SimpleConfiguration
22
+ # def name= tool_name
23
+ # raise Patir::ConfigurationException,"Inappropriate language not allowed" if tool_name=="@#!&@&$}"
24
+ # @name=tool_name
25
+ # end
26
+ # end
27
+ #
28
+ # class SimpleConfigurator
29
+ # include SimpleConfiguration
30
+ #
31
+ # def configuration
32
+ # return @name
33
+ # end
34
+ # end
35
+ #The configuration file would then be
36
+ # configuration.name="really polite name"
37
+ #To use it you would do
38
+ # cfg=SimpleConfigurator.new("config.cfg").configuration
39
+ class Configurator
40
+ attr_reader :logger,:config_file,:wd
41
+ def initialize config_file,logger=nil
42
+ @logger=logger
43
+ @logger||=Patir.setup_logger
44
+ @config_file=config_file
45
+ load_configuration(@config_file)
46
+ end
47
+
48
+ #Returns self. This should be overriden in the actual implementations
49
+ def configuration
50
+ return self
51
+ end
52
+
53
+ #Loads the configuration from a file
54
+ #
55
+ #Use this to chain configuration files together
56
+ #==Example
57
+ #Say you have on configuration file "first.cfg" that contains all the generic directives and several others that change only one or two things.
58
+ #
59
+ #You can 'include' the first.cfg file in the other configurations with
60
+ # configuration.load_from_file("first.cfg")
61
+ def load_from_file filename
62
+ fnm = File.exist?(filename) ? filename : File.join(@wd,filename)
63
+ load_configuration(fnm)
64
+ end
65
+ private
66
+ def load_configuration filename
67
+ begin
68
+ cfg_txt=File.read(filename)
69
+ @wd=File.expand_path(File.dirname(filename))
70
+ configuration=self
71
+ #add the path to the require lookup path to allow require statements in the configuration files
72
+ $:.unshift File.join(@wd)
73
+ #evaluate in the working directory to enable relative paths in configuration
74
+ Dir.chdir(@wd){eval(cfg_txt,binding())}
75
+ @logger.info("Configuration loaded from #{filename}") if @logger
76
+ rescue ConfigurationException
77
+ #pass it on, do not wrap again
78
+ raise
79
+ rescue SyntaxError
80
+ #Just wrap the exception so we can differentiate
81
+ @logger.debug($!)
82
+ raise ConfigurationException.new,"Syntax error in the configuration file '#{filename}':\n#{$!.message}"
83
+ rescue NoMethodError
84
+ @logger.debug($!)
85
+ raise ConfigurationException.new,"Encountered an unknown directive in configuration file '#{filename}':\n#{$!.message}"
86
+ rescue
87
+ @logger.debug($!)
88
+ #Just wrap the exception so we can differentiate
89
+ raise ConfigurationException.new,"#{$!.message}"
90
+ end
91
+ end
92
+ end
93
93
  end