rio 0.3.3 → 0.3.4

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 (88) hide show
  1. data/ChangeLog +225 -0
  2. data/README +12 -0
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/doc/ANNOUNCE +160 -71
  6. data/doc/RELEASE_NOTES +71 -2
  7. data/ex/colx.rb +1 -1
  8. data/ex/passwd_report.rb +4 -8
  9. data/ex/riocat +5 -5
  10. data/ex/riogunzip +1 -1
  11. data/ex/riogzip +6 -6
  12. data/ex/rioprompt.rb +6 -0
  13. data/lib/rio.rb +3 -13
  14. data/lib/rio/arycopy.rb +1 -1
  15. data/lib/rio/base.rb +1 -5
  16. data/lib/rio/construct.rb +75 -0
  17. data/lib/rio/constructor.rb +42 -11
  18. data/lib/rio/context.rb +1 -1
  19. data/lib/rio/context/dir.rb +50 -23
  20. data/lib/rio/context/methods.rb +5 -3
  21. data/lib/rio/{cxdir.rb → context/skip.rb} +24 -36
  22. data/lib/rio/context/stream.rb +38 -16
  23. data/lib/rio/cp.rb +24 -5
  24. data/lib/rio/dir.rb +8 -7
  25. data/lib/rio/doc/HOWTO.rb +33 -33
  26. data/lib/rio/doc/INTRO.rb +416 -256
  27. data/lib/rio/doc/MISC.rb +3 -1
  28. data/lib/rio/doc/SYNOPSIS.rb +28 -33
  29. data/lib/rio/entrysel.rb +76 -9
  30. data/lib/rio/file.rb +2 -1
  31. data/lib/rio/filter.rb +95 -0
  32. data/lib/rio/filter/closeoneof.rb +1 -1
  33. data/lib/rio/grande.rb +0 -74
  34. data/lib/rio/if.rb +2 -1
  35. data/lib/rio/if/basic.rb +1 -1
  36. data/lib/rio/if/csv.rb +1 -1
  37. data/lib/rio/if/dir.rb +1 -220
  38. data/lib/rio/if/fileordir.rb +26 -12
  39. data/lib/rio/if/grande.rb +55 -6
  40. data/lib/rio/if/grande_entry.rb +355 -0
  41. data/lib/rio/if/{methods.rb → grande_stream.rb} +69 -88
  42. data/lib/rio/if/path.rb +25 -3
  43. data/lib/rio/if/stream.rb +62 -37
  44. data/lib/rio/if/temp.rb +2 -2
  45. data/lib/rio/if/test.rb +23 -0
  46. data/lib/rio/impl/path.rb +5 -0
  47. data/lib/rio/match.rb +6 -3
  48. data/lib/rio/matchrecord.rb +50 -46
  49. data/lib/rio/{filter/chomp.rb → ops/construct.rb} +12 -20
  50. data/lib/rio/ops/create.rb +3 -0
  51. data/lib/rio/ops/dir.rb +12 -6
  52. data/lib/rio/ops/either.rb +17 -3
  53. data/lib/rio/ops/path.rb +4 -1
  54. data/lib/rio/ops/stream/input.rb +6 -1
  55. data/lib/rio/ops/stream/read.rb +1 -3
  56. data/lib/rio/{context/chomp.rb → prompt.rb} +17 -13
  57. data/lib/rio/rl/base.rb +1 -1
  58. data/lib/rio/rl/builder.rb +3 -1
  59. data/lib/rio/state.rb +7 -13
  60. data/lib/rio/stream.rb +8 -5
  61. data/lib/rio/stream/open.rb +1 -1
  62. data/lib/rio/version.rb +1 -1
  63. data/test/mswin32.rb +1 -1
  64. data/test/runtests_gem.rb +1 -1
  65. data/test/tc/all.rb +3 -0
  66. data/test/tc/copy-from.rb +13 -13
  67. data/test/tc/copy-to.rb +1 -1
  68. data/test/tc/copy.rb +1 -1
  69. data/test/tc/copydir.rb +0 -24
  70. data/test/tc/copysymlink.rb +39 -0
  71. data/test/tc/csv.rb +2 -2
  72. data/test/tc/csv2.rb +4 -4
  73. data/test/tc/misc.rb +16 -16
  74. data/test/tc/nolines.rb +26 -26
  75. data/test/tc/noqae.rb +74 -74
  76. data/test/tc/overload.rb +28 -28
  77. data/test/tc/riorl.rb +36 -0
  78. data/test/tc/selnosel.rb +36 -0
  79. data/test/tc/skip.rb +58 -0
  80. data/test/tc/skiplines.rb +42 -0
  81. data/test/tc/symlink.rb +1 -1
  82. data/test/tc/symlink0.rb +1 -1
  83. data/test/tc/temp.rb +1 -1
  84. data/test/tc/tempdir.rb +1 -1
  85. data/test/tc/testcase.rb +7 -1
  86. metadata +14 -8
  87. data/lib/rio/matchcolumns.rb +0 -266
  88. data/lib/rio/rangemath.rb +0 -44
@@ -76,6 +76,7 @@ which can be chained to a Rio method as in this example or stored in a variable.
76
76
  array = ario.readlines
77
77
 
78
78
  ario = rio('afile')
79
+
79
80
  In this case the resource specified is a relative path. After the first line
80
81
  the Rio does know or care whether it
81
82
  is a path to a file nor whether it exists. Rio provides many methods that only deal with a resource
@@ -93,6 +94,7 @@ The rio constructor can be used to specify non-file-system resources, but for th
93
94
  our discussion to paths to entities on file-systems.
94
95
 
95
96
  array = ario.readlines
97
+
96
98
  Now that we have a Rio, we can call one of its methods; in this case _readlines_. This is an example of using
97
99
  a Rio as a proxy for the builtin IO#readlines. Given the method _readlines_, the Rio opens 'afile' for reading,
98
100
  calls readlines on the resulting IO object, closes the IO object, and returns the lines read.
@@ -131,7 +133,7 @@ a second copy, we might do something like this:
131
133
  afile.rewind > rio('acopy2')
132
134
  afile.close
133
135
 
134
- Actually the 'thinking-process' of the Rio when it sees a copy-to operator is much more complex that described above.
136
+ Actually the 'thinking process' of the Rio when it sees a copy-to operator is much more complex that described above.
135
137
  If its argument had been a rio referencing a directory, it would not have opened itself for reading,
136
138
  but instead used FileUtils#cp to copy itself; if its argument had been a string, its contents would have ended up
137
139
  in the string; If its argument had been an array, its lines would been elements of that array; if its argument had
@@ -60,30 +60,37 @@ For the following assume:
60
60
  astring = ""
61
61
  anarray = []
62
62
 
63
- Copy a file into a string
64
- rio('afile') > astring
63
+ Copy or append a file to a string
64
+ rio('afile') > astring # copy
65
+ rio('afile') >> astring # append
65
66
 
66
- Copy a string into a file
67
- rio('afile') < astring
67
+ Copy or append a string to a file
68
+ rio('afile') < astring # copy
69
+ rio('afile') << astring # append
68
70
 
69
- Copy the chomped lines of a file into an array
70
- rio('afile').chomp > anarray
71
+ Copy or append the lines of a file to an array
72
+ rio('afile') > anarray
73
+ rio('afile') >> anarray
71
74
 
72
- Copy a file into another file
73
- rio('afile') > rio('another_file')
75
+ Copy or append a file to another file
76
+ rio('afile') > rio('another_file')
77
+ rio('afile') >> rio('another_file')
74
78
 
75
- Copy a file into a directory
76
- rio('afile') > rio('adir')
79
+ Copy a file to a directory
80
+ rio('adir') << rio('afile')
77
81
 
78
- Copy an entire directory structure into another directory
79
- rio('adir') > rio('another_directory')
82
+ Copy a directory structure to another directory
83
+ rio('adir') >> rio('another_directory')
80
84
 
81
- Copy a web page into a file
85
+ Copy a web-page to a file
82
86
  rio('http://rubydoc.org/') > rio('afile')
83
87
 
84
- Copy a file from a ftp server into a local file
85
- rio('ftp://host/afile.gz') > rio('afile.gz')
86
-
88
+ Ways to get the chomped lines of a file into an array
89
+ anarray = rio('afile').chomp[] # subscript operator
90
+ rio('afile').chomp > anarray # copy-to operator
91
+ anarray = rio('afile').chomp.to_a # to_a
92
+ anarray = rio('afile').chomp.readlines # IO#readlines
93
+
87
94
  Copy a gzipped file un-gzipping it
88
95
  rio('afile.gz').gzip > rio('afile')
89
96
 
@@ -102,11 +109,8 @@ Iterate over only the files in a directory
102
109
  Iterate over only the .rb files in a directory
103
110
  rio('adir').files('*.rb') { |entrio| ... }
104
111
 
105
- Iterate over only the directories in a directory
106
- rio('adir').dirs { |entrio| ... }
107
-
108
112
  Iterate over only the _dot_ files in a directory
109
- rio('adir').dirs(/^\./) { |entrio| ... }
113
+ rio('adir').files(/^\./) { |entrio| ... }
110
114
 
111
115
  Iterate over the files in a directory and its subdirectories, skipping '.svn' and 'CVS' directories
112
116
  rio('adir').norecurse(/^\.svn$/,'CVS').files { |entrio| ... }
@@ -114,24 +118,15 @@ Iterate over the files in a directory and its subdirectories, skipping '.svn' an
114
118
  Create an array of the .rb entries in a directory
115
119
  anarray = rio('adir')['*.rb']
116
120
 
117
- Iterate over the .rb files in a directory and its subdirectories
118
- rio('adir').all.files('*.rb') { |entrio| ... }
119
-
120
121
  Create an array of the .rb entries in a directory and its subdirectories
121
122
  anarray = rio('adir').all['*.rb']
122
123
 
123
- Create an array of the .rb files in a directory and its subdirectories
124
- anarray = rio('adir').all.files['*.rb']
124
+ Iterate over the .rb files in a directory and its subdirectories
125
+ rio('adir').all.files('*.rb') { |entrio| ... }
125
126
 
126
127
  Copy an entire directory structure and the .rb files within it
127
128
  rio('adir').dirs.files('*.rb') > rio('another_directory')
128
129
 
129
- Iterate over the chomped lines of a file
130
- rio('afile').chomp.lines { |line| ... }
131
-
132
- Put the chomped lines of a file into an array
133
- anarray = rio('afile').chomp.lines[]
134
-
135
130
  Iterate over the first 10 chomped lines of a file
136
131
  rio('afile').chomp.lines(0..9) { |line| ... }
137
132
 
@@ -154,10 +149,10 @@ Put chomped lines that start with 'Rio' into an array
154
149
  anarray = rio('afile').chomp[/^Rio/]
155
150
 
156
151
  Iterate over the non-empty, non-comment chomped lines of a file
157
- rio('afile').chomp.nolines(:empty?,/^\s*#/) { |line| ... }
152
+ rio('afile').chomp.skiplines(:empty?,/^\s*#/) { |line| ... }
158
153
 
159
154
  Copy the output of th ps command into an array, skipping the header line and the ps command entry
160
- rio(?-,'ps -a').nolines(0,/ps$/) > anarray
155
+ rio(?-,'ps -a').skiplines(0,/ps$/) > anarray
161
156
 
162
157
  Prompt for input and return what was typed
163
158
  ans = rio(?-).print("Type Something: ").chomp.gets
@@ -54,7 +54,7 @@ module RIO
54
54
  end
55
55
  def ===(el) self =~ el end
56
56
  abstract_method :=~
57
-
57
+
58
58
  end
59
59
  class Any < Base
60
60
  def =~(entry) true end
@@ -74,6 +74,14 @@ module RIO
74
74
  class Symbol < Base
75
75
  def =~(entry) entry.__send__(@match_to) end
76
76
  end
77
+ class And < Base
78
+ def initialize(matches)
79
+ super(matches.flatten.map { |arg| Match::Entry.create(arg) })
80
+ end
81
+ def =~(el)
82
+ (@match_to.empty? or @match_to.all? { |sel| sel =~ el })
83
+ end
84
+ end
77
85
  def create(arg)
78
86
  case arg
79
87
  when ::String then Glob.new(arg)
@@ -82,6 +90,7 @@ module RIO
82
90
  when ::Symbol then Symbol.new(arg)
83
91
  when ::TrueClass then Any.new(arg)
84
92
  when ::FalseClass then None.new(arg)
93
+ when ::Array then And.new(arg)
85
94
  else raise ArgumentError,"a String,Regexp,Proc or Symbol is required (#{arg})"
86
95
  end
87
96
  end
@@ -93,9 +102,6 @@ module RIO
93
102
  module Match
94
103
  module Entry
95
104
  class List
96
- def callstr(func,*args)
97
- self.class.to_s+'['+self.to_s+']'+'.'+func.to_s+'('+args.join(',')+')'
98
- end
99
105
  attr_reader :sym
100
106
  attr_accessor :list
101
107
  def initialize(sym,*args)
@@ -116,9 +122,14 @@ module RIO
116
122
  end
117
123
  extend Forwardable
118
124
  def_instance_delegators(:@list,:each)
125
+ def callstr(func,*args)
126
+ self.class.to_s+'['+self.to_s+']'+'.'+func.to_s+'('+args.join(',')+')'
127
+ end
119
128
  end
120
129
  class Sels < Array
121
130
  def <<(entry_list)
131
+ #p callstr('<<',entry_list)
132
+
122
133
  same_sym = self.grep(entry_list)
123
134
  if same_sym.empty?
124
135
  super
@@ -126,11 +137,59 @@ module RIO
126
137
  same_sym[0].list = entry_list.list
127
138
  end
128
139
  end
140
+ def callstr(func,*args)
141
+ self.class.to_s+'['+self.to_s+']'+'.'+func.to_s+'('+args.join(',')+')'
142
+ end
129
143
  end
130
144
  class Selector
131
- def initialize(sel,nosel)
132
- @sel = sel
133
- @nosel = nosel
145
+ attr_reader :sel,:nosel
146
+ def initialize(entry_sel)
147
+ @entry_sel = entry_sel
148
+ @sel = @nosel = nil
149
+ process_entry_sel() if @entry_sel
150
+ end
151
+ def es_args()
152
+ es = @entry_sel
153
+ es['args']
154
+ end
155
+ def something_selected?
156
+ %w[entries files dirs].any? { |k| es_args.has_key?(k) }
157
+ end
158
+ def something_skipped?
159
+ %w[skipentries skipfiles skipdirs].any? { |k| es_args.has_key?(k) }
160
+ end
161
+ def skip_type(skip_args)
162
+
163
+ end
164
+ def process_entry_sel()
165
+ ea = es_args
166
+ raise RuntimeError, "Internal error: entry_sel_args not set" unless ea
167
+ if something_selected?
168
+ @sel = Match::Entry::Sels.new
169
+ @sel << Match::Entry::List.new(:true?,*ea['entries']) if ea.has_key?('entries')
170
+ @sel << Match::Entry::List.new(:file?,*ea['files']) if ea.has_key?('files')
171
+ @sel << Match::Entry::List.new(:dir?,*ea['dirs']) if ea.has_key?('dirs')
172
+ end
173
+ if something_skipped?
174
+ @nosel = Match::Entry::Sels.new
175
+ if ea.has_key?('skipentries')
176
+ @nosel << Match::Entry::List.new(:true?,*ea['skipentries'])
177
+ end
178
+ if ea.has_key?('skipfiles')
179
+ @nosel << Match::Entry::List.new(:file?,*ea['skipfiles'])
180
+ unless ea['skipfiles'].empty? or ea.has_key?('files')
181
+ @sel ||= Match::Entry::Sels.new
182
+ @sel << Match::Entry::List.new(:file?)
183
+ end
184
+ end
185
+ if ea.has_key?('skipdirs')
186
+ @nosel << Match::Entry::List.new(:dir?,*ea['skipdirs'])
187
+ unless ea['skipdirs'].empty? or ea.has_key?('dirs')
188
+ @sel ||= Match::Entry::Sels.new
189
+ @sel << Match::Entry::List.new(:dir?)
190
+ end
191
+ end
192
+ end
134
193
  end
135
194
  def inspect()
136
195
  str = sprintf('#<Selector:0x%08x',self.object_id)
@@ -144,7 +203,7 @@ module RIO
144
203
 
145
204
  def yes?(el)
146
205
  @sel.nil? or @sel.detect { |match_entry| match_entry =~ el }
147
- # @sel.nil? or @sel.grep(el)
206
+ # @sel.nil? or @sel.grep(el)
148
207
  end
149
208
  def no?(el)
150
209
  @nosel.detect { |match_entry| match_entry =~ el } unless @nosel.nil?
@@ -155,8 +214,16 @@ module RIO
155
214
  def match?(el)
156
215
  yes?(el) and not no?(el)
157
216
  end
217
+ def callstr(func,*args)
218
+ self.class.to_s+'['+self.to_s+']'+'.'+func.to_s+'('+args.join(',')+')'
219
+ end
220
+ end
221
+ class SelectorClassic < Selector
222
+ def initialize(sel,nosel)
223
+ @sel = sel
224
+ @nosel = nosel
225
+ end
158
226
  end
159
227
  end
160
-
161
228
  end
162
229
  end
@@ -64,7 +64,8 @@ module RIO
64
64
  def each(*args,&block)
65
65
  #p "#{callstr('each',*args)} ss_type=#{ss_type?}"
66
66
  if ss_type? == 'files' and !stream_iter?
67
- sel = Match::Entry::Selector.new(cx['sel'],cx['nosel'])
67
+ #sel = Match::Entry::Selector.new(cx['sel'],cx['nosel'])
68
+ sel = Match::Entry::Selector.new(cx['entry_sel'])
68
69
  yield new_rio_cx(self) if sel.match?(self)
69
70
  else
70
71
  fstream.each(*args,&block)
@@ -0,0 +1,95 @@
1
+ #--
2
+ # ===============================================================================
3
+ # Copyright (c) 2005, Christopher Kleckner
4
+ # All rights reserved
5
+ #
6
+ # This file is part of the Rio library for ruby.
7
+ #
8
+ # Rio is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # Rio is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with Rio; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ # ===============================================================================
22
+ #++
23
+ #
24
+ # To create the documentation for Rio run the command
25
+ # rake rdoc
26
+ # from the distribution directory. Then point your browser at the 'doc/rdoc' directory.
27
+ #
28
+ # Suggested Reading
29
+ # * RIO::Doc::SYNOPSIS
30
+ # * RIO::Doc::INTRO
31
+ # * RIO::Doc::HOWTO
32
+ # * RIO::Rio
33
+ #
34
+ # <b>Rio is pre-alpha software.
35
+ # The documented interface and behavior is subject to change without notice.</b>
36
+
37
+
38
+ module RIO
39
+ module Filter #:nodoc: all
40
+ def self.make_line_filter(sym)
41
+ module_eval %{
42
+ module #{sym.to_s.capitalize}
43
+ module IOE
44
+ def gets(*args) super.__send__(:#{sym}) end
45
+ def readline(*args) super.__send__(:#{sym}) end
46
+ def each_line(*args,&block) super { |l| yield l.__send__(:#{sym}) } end
47
+ def readlines(*args) super.map(&:#{sym}) end
48
+ end
49
+ include IOE
50
+ end
51
+ }
52
+ end
53
+ end
54
+ end
55
+ module RIO
56
+ module Cx
57
+ module Methods
58
+ require 'rio/context/cxx.rb'
59
+ def self.make_filter_methods(sym)
60
+ module_eval %{
61
+ def #{sym}(arg=true,&block)
62
+ cx['#{sym}'] = arg
63
+ add_filter(Filter::#{sym.to_s.capitalize}) if arg and self.ioh
64
+ each(&block) if block_given?
65
+ self
66
+ end
67
+ def #{sym}?() cxx?('#{sym}') end
68
+ def no#{sym}(arg=false,&block) #{sym}(arg,&block) end
69
+ }
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ module RIO
76
+ FILTER_SYMS = [:chomp, :strip, :lstrip, :rstrip]
77
+ FILTER_SYMS.each { |sym|
78
+ Filter.make_line_filter(sym)
79
+ Cx::Methods.make_filter_methods(sym)
80
+ }
81
+ module Stream
82
+ module Filters
83
+
84
+ def add_line_filters()
85
+ add_filter(Filter::Chomp) if chomp?
86
+ add_filter(Filter::Strip) if strip?
87
+ add_filter(Filter::Lstrip) if lstrip?
88
+ add_filter(Filter::Rstrip) if rstrip?
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+
95
+ __END__
@@ -70,7 +70,7 @@ module RIO
70
70
  #p callstr('copy_stream',dst)
71
71
  close_on_eof(super)
72
72
  end
73
- def slurp()
73
+ def contents()
74
74
  close_on_eof(super)
75
75
  end
76
76
  def readlines(*args)
@@ -75,77 +75,3 @@ module RIO
75
75
 
76
76
  end
77
77
  end
78
- module RIO
79
- module Grande
80
- module Dir
81
-
82
- def each_(*args,&block)
83
- #p "#{callstr('each_',*args)} sel=#{cx['sel'].inspect} nosel=#{cx['nosel'].inspect}"
84
- sel = Match::Entry::Selector.new(cx['sel'],cx['nosel'])
85
- selfstr = (self.to_s == '.' ? nil : self.to_s)
86
- self.ioh.each do |estr|
87
- next if estr =~ /^\.(\.)?$/
88
- begin
89
- erio = new_rio_cx(selfstr ? Impl::U.join(selfstr,estr) : estr )
90
-
91
- if stream_iter?
92
- _add_stream_iter_cx(erio).each(&block) if erio.file? and sel.match?(erio)
93
- else
94
- yield _add_iter_cx(erio) if sel.match?(erio)
95
- end
96
-
97
- if cx.has_key?('all') and erio.dir?
98
- rsel = Match::Entry::Selector.new(cx['r_sel'],cx['r_nosel'])
99
- _add_recurse_iter_cx(erio).each(&block) if rsel.match?(erio)
100
- end
101
-
102
- rescue ::Errno::ENOENT, ::URI::InvalidURIError => ex
103
- $stderr.puts(ex.message+". Skipping.")
104
- end
105
- end
106
- closeoneof? ? self.close.softreset : self
107
- end
108
-
109
-
110
-
111
- private
112
-
113
- def _ss_keys() Cx::SS::ENTRY_KEYS + Cx::SS::STREAM_KEYS end
114
- CX_ALL_SKIP_KEYS = ['retrystate']
115
- def _add_recurse_iter_cx(ario)
116
- new_cx = ario.cx
117
- cx.keys.reject { |k| CX_ALL_SKIP_KEYS.include?(k) }.each { |k|
118
- new_cx.set_(k,cx[k])
119
- }
120
- ario.cx = new_cx
121
- ario
122
- end
123
- def _add_cx(ario,keys)
124
- new_cx = ario.cx
125
- keys.each {|k|
126
- next unless cx.has_key?(k)
127
- new_cx.set_(k,cx[k])
128
- }
129
- ario.cx = new_cx
130
- end
131
- CX_DIR_ITER_KEYS = %w[sel nosel]
132
- CX_STREAM_ITER_KEYS = %w[stream_rectype stream_itertype stream_sel stream_nosel]
133
- def _add_iter_cx(ario)
134
- if nostreamenum?
135
- _add_cx(ario,CX_DIR_ITER_KEYS)
136
- end
137
- _add_stream_iter_cx(ario)
138
- end
139
- def _add_stream_iter_cx(ario)
140
- _add_cx(ario,CX_STREAM_ITER_KEYS)
141
- new_cx = ario.cx
142
- if stream_iter?
143
- new_cx.set_('ss_args',cx['ss_args']) if cx.has_key?('ss_args')
144
- new_cx.set_('ss_type',cx['ss_type']) if cx.has_key?('ss_type')
145
- end
146
- ario.cx = new_cx
147
- ario
148
- end
149
- end
150
- end
151
- end