cocos 0.4.0 → 0.4.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f34f86372be24c8d19abde781cc66b5d9c6b323e09c7620221f30b9e115b786d
4
- data.tar.gz: d025bedc0c96372e74339bd4a0ae7fcd56857d23a1678b35c1b2a8aef2b1be12
3
+ metadata.gz: e276117f95b89fcfc61f23559b725c3905aa8668eb6b0b7e7175c4fe57eb1a86
4
+ data.tar.gz: 1b3043bd0de213189a8e531d4fea92ccf7b1262636067a05c914261a66d4338c
5
5
  SHA512:
6
- metadata.gz: 50b9cc405bb2d5e16900958a55c11fcf02b5854a4410c072cbab51d585c3a656f9a9a8bb4246b51fc7b46d0200d7fefa98ea3bd4d0e95bb455c8e60dc92c6e10
7
- data.tar.gz: fc797f93f3f73644b5b6ee6695564ffd4f4f7308aa8412d5d62785baa9301e5cd740ceb017215d7a3417884edcc057044e15c938bea1310c4c217cbb310d188b
6
+ metadata.gz: ff35b353cde05f6f76e6b6069442abda8769488768edaba6dfd2b411b06ec24aac97d0a10c25bcac08ba5617451e2a9c800861a5bd535e4d347b8e68b17eb747
7
+ data.tar.gz: '08c86a7e5950818e53c38668b68259e4248be7e0ef5201a52a570349e7dfe803dd6a534af73ca45ca7745fadc9a19022c15d08a38a121c5153fbc7705035e153'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.4.0
1
+ ### 0.4.2
2
2
  ### 0.0.1 / 2022-08-01
3
3
 
4
4
  * Everything is new. First release.
data/Manifest.txt CHANGED
@@ -5,4 +5,5 @@ README.md
5
5
  Rakefile
6
6
  lib/cocos.rb
7
7
  lib/cocos/env.rb
8
+ lib/cocos/find_file.rb
8
9
  lib/cocos/version.rb
data/README.md CHANGED
@@ -50,7 +50,7 @@ txt = File.read( "history.txt" )
50
50
  # (e.g. on microsoft windows it is ISO Code Page (CP-1252
51
51
  # or something - depending on your locale/culture/language)
52
52
 
53
- txt = File.open( "history.txt", "r:utf-8" ) do |f|
53
+ txt = File.open( "history.txt", "r:bom|utf-8" ) do |f|
54
54
  f.read
55
55
  end
56
56
  ```
@@ -61,7 +61,7 @@ and always repeating the same open / read and code block dance
61
61
  again and again e.g.:
62
62
 
63
63
  ``` ruby
64
- txt = File.open( "history.json", "r:utf-8" ) do |f|
64
+ txt = File.open( "history.json", "r:bom|utf-8" ) do |f|
65
65
  f.read
66
66
  end
67
67
  data = JSON.parse( txt )
@@ -94,7 +94,7 @@ _Read / parse convenience short-cut helpers_
94
94
  also known as `read_txt`
95
95
 
96
96
 
97
- `read_lines( path )`
97
+ `read_lines( path, chomp: true|false )`
98
98
 
99
99
 
100
100
  `read_json( path )` / `parse_json( str )`
@@ -138,5 +138,4 @@ That's it for now.
138
138
  ## License
139
139
 
140
140
  The `cocos` scripts are dedicated to the public domain.
141
- Use it as you please with no restrictions whatsoever.
142
-
141
+ Use as you please with no restrictions whatsoever.
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ Hoe.spec 'cocos' do
12
12
  self.urls = { home: 'https://github.com/rubycocos/cocos' }
13
13
 
14
14
  self.author = 'Gerald Bauer'
15
- self.email = 'gerald.bauer@gmail.com'
15
+ self.email = 'gerald.bauer@gmail.com'
16
16
 
17
17
  # switch extension to .markdown for gihub formatting
18
18
  self.readme_file = 'README.md'
data/lib/cocos/env.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # simple read_env, load_env machinery
3
3
  ## inspired by
4
4
  ## dotenv gem -> https://github.com/bkeepers/dotenv
5
- ## figaro -> https://github.com/laserlemon/figaro
5
+ ## figaro -> https://github.com/laserlemon/figaro
6
6
  ## and others
7
7
 
8
8
 
@@ -13,60 +13,65 @@ module EnvParser
13
13
  # returns a hash
14
14
  # (compatible structure - works like YAML.load_file)
15
15
  #
16
- # change to .read(path) and parse( text) - why? why not?
16
+ # change to .read(path) and parse(text) - why? why not?
17
17
  def self.load_file( path )
18
- text = File.open( path, 'r:utf-8' ) { |f| f.read }
18
+ text = File.open( path, 'r:bom|utf-8', newline: :lf) { |f| f.read }
19
19
  parse( text )
20
20
  end
21
21
  def self.load( text ) parse( text ); end
22
-
23
-
22
+
23
+
24
24
  class Error < StandardError; end
25
-
25
+
26
26
  ## todo/check - what is JSON and YAML returning Parser/ParseError something else?
27
27
  ## YAML uses ParseError and JSON uses ParserError
28
28
  class ParseError < Error; end
29
-
29
+
30
30
 
31
31
  ## todo/check - if support for empty values e.g. abc= is required/possible???
32
32
  ## todo/ addd support for quoted values - why? why not?
33
33
  ## add support for "inline" end of line comments - why? why not?
34
34
  ## add support for escapes and multi-line values - why? why not?
35
- LINE_RX = /\A(?<key>[A-Za-z][A-Za-z0-9_-]*)
35
+ LINE_RE = %r{\A
36
+ [ ]*
37
+ (?<key> [A-Za-z][A-Za-z0-9_-]*)
36
38
  [ ]*
37
39
  =
38
40
  [ ]*
39
41
  (?<value>.+?) ## non-greedy
42
+ [ ]*
40
43
  \z
41
- /x
44
+ }x
42
45
 
43
- ## use a parser class - why? why not?
44
- def self.parse( text )
45
- h = {}
46
+ ## use a parser class - why? why not?
47
+ def self.parse( text )
48
+ h = {}
46
49
 
47
- lineno = 0
50
+ lineno = 0
48
51
  text.each_line do |line|
49
52
  lineno += 1 ## track line numbers for (parse) error reporting
50
-
51
53
  line = line.strip ## check: use strip (or be more strict) - why? why not?
54
+
52
55
  ## skip empty and comment lines
53
- next if line.empty? || line.start_with?( '#' )
56
+ next if line.empty? || line.start_with?( '#' )
57
+ ## support __END__ marker for inline comments
58
+ break if line == '__END__'
54
59
 
55
- if m=LINE_RX.match(line)
60
+ if m=LINE_RE.match(line)
56
61
  key = m[:key]
57
62
  value = m[:value]
58
-
63
+
59
64
  ## todo/check - check/warn about duplicates - why? why not?
60
65
  h[key] = value
61
- else
66
+ else
62
67
  raise ParseError, "line #{lineno} - unknown line type; cannot parse >#{line}<"
63
- end
68
+ end
64
69
  end
65
70
  h
66
71
  end # methdod self.parse
67
72
  end # module EnvParser
68
73
 
69
-
74
+
70
75
 
71
76
  __END__
72
77
 
@@ -0,0 +1,83 @@
1
+
2
+
3
+ ##
4
+ ## note - use File.file? instead of File.exist?
5
+ ## (checks if file exists AND file is a file NOT a directory)
6
+ ##
7
+ ##
8
+ ## add option - raise_on_error: false - why? why not?
9
+ ## def find_file! - find_file( raise_on_error: false )
10
+ ##
11
+ ## todo/check
12
+ ## golang lookup_path or such
13
+ ## always return absolute (expanded) path - why? why not?
14
+
15
+
16
+ module Kernel
17
+
18
+ def find_file!( name, path: )
19
+ filepath = find_file( name, path: path )
20
+ raise Errno::ENOENT, "file #{name.inspect} not found; looking in path #{path.inspect}" if filepath.nil?
21
+ filepath
22
+ end
23
+
24
+ ##
25
+ ## note - find_file will NOT find directories!!!
26
+ ## File.file? will only check if a file (not directory) exits!!
27
+
28
+ ##
29
+ ## todo/check - expand path and use File.realpath too?
30
+ ## or keep "simple" File.join ?
31
+
32
+ ##
33
+ ## note - always expand path for now and return absolute path!
34
+ ##
35
+ ## note - do NOT search path if name passed in is absolute!!!
36
+ def find_file( name, path: )
37
+ filepath = File.expand_path( name )
38
+ return filepath if File.file?( filepath )
39
+
40
+ ## note - if name starts with root / or c:\
41
+ ## assume it's absolute - handle by Pathname lib for now
42
+ ## do NOT search!!!
43
+ return nil if Pathname.new(name).absolute?
44
+
45
+ path.each do |basedir|
46
+ filepath = File.expand_path( name, basedir )
47
+ return filepath if File.file?( filepath )
48
+ end
49
+
50
+ nil ## return nil if not found
51
+ end
52
+
53
+
54
+
55
+ def find_dir( name, path: [] )
56
+ dirpath = File.expand_path( name )
57
+ return dirpath if Dir.exist?( dirpath )
58
+
59
+ ## note - if name starts with / or \ assume it's absolute!!
60
+ ## do NOT search!!!
61
+ ## note - search still works for
62
+ ## ./austria or ../austria or such
63
+ ##
64
+ ## todo/check/fix-fix-fix
65
+ ## is there a File.absolute? or such method for reuse??
66
+ ##
67
+ ## todo/fix-fix-fix add absolute check upstream to find_file too!!!
68
+ ## return nil if name.start_with?( %r{[/\\]} )
69
+ ## note - Pathname#absolute? is basically !Pathname#relative? !!!
70
+ return nil if Pathname.new(name).absolute?
71
+
72
+ path.each do |basedir|
73
+ ## todo/check - always make sure basedir is an absolute/expanded path - why? why not?
74
+ dirpath = File.expand_path( name, basedir )
75
+ return dirpath if Dir.exist?( dirpath )
76
+ end
77
+
78
+ nil ## return nil if not found
79
+ end
80
+
81
+
82
+
83
+ end # module Kernel
data/lib/cocos/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module Cocos
3
3
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
4
  MINOR = 4
5
- PATCH = 0
5
+ PATCH = 2
6
6
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
7
 
8
8
  def self.version
data/lib/cocos.rb CHANGED
@@ -8,6 +8,8 @@ require 'json'
8
8
  require 'yaml'
9
9
  require 'base64' ## e.g. Base64.decode64,Base64.encode64,...
10
10
  require 'fileutils'
11
+ require 'pathname' ## used by find_file/find_dir to check for absolute? etc.
12
+
11
13
 
12
14
  require 'uri'
13
15
  require 'net/http'
@@ -33,13 +35,15 @@ require_relative 'cocos/version' # note: let version always go first
33
35
  require_relative 'cocos/env' ## e.g. EnvParser
34
36
 
35
37
 
38
+ require_relative 'cocos/find_file'
39
+
40
+
36
41
  ###
37
42
  ## read/parse convenience/helper shortcuts
38
43
 
39
44
 
40
- module Kernel
41
-
42
45
 
46
+ module Kernel
43
47
 
44
48
  ################
45
49
  # private helpers - keep along here - why? why not?
@@ -48,6 +52,20 @@ module Kernel
48
52
  ## todo: add symbolize options a la read_json? - why? why not?
49
53
  ## add sep options
50
54
 
55
+ ##
56
+ ## todo - add upstream
57
+ ## to CsvHash.read
58
+ ## newline option on read/write and
59
+ ## bom option on read
60
+ ## note:
61
+ ## (i) :newline => :lf option
62
+ # Reads text natively; normalizes all incoming newlines to strict LF (\n)
63
+ ## (ii) 'bom|' flag
64
+ ## The 'bom|' flag instructs Ruby to drop the BOM bytes if they exist
65
+ ## File.open( path, 'r:bom|utf-8', newline: :lf) do |f|
66
+
67
+
68
+
51
69
  def read_csv( path, sep: nil )
52
70
  opts = {}
53
71
  opts[:sep] = sep if sep
@@ -105,6 +123,10 @@ def download_tab( url )
105
123
  end
106
124
 
107
125
 
126
+ ## fix-fix-fix
127
+ ## for json add new strict: true|false option (default is false!)
128
+ ## add option for allowing trailing_commas and comments!!!
129
+ ## see JSON.parse for more
108
130
 
109
131
  ## todo: add symbolize options ???
110
132
  def read_json( path )
@@ -151,20 +173,19 @@ def download_ini( url )
151
173
  parse_ini( download_text( url ))
152
174
  end
153
175
 
154
- alias_method :read_conf, :read_ini
155
- alias_method :parse_conf, :parse_ini
176
+ alias_method :read_conf, :read_ini
177
+ alias_method :parse_conf, :parse_ini
156
178
  alias_method :download_conf, :download_ini
157
179
 
158
180
 
159
181
 
160
-
161
182
  def read_text( path )
162
- ## todo/check: add universal newline mode or such?
163
- ## e.g. will always convert all
164
- ## newline variants (\n|\r|\n\r) to "universal" \n only
165
- ##
166
- ## add r:bom - why? why not?
167
- File.open( path, 'r:utf-8' ) do |f|
183
+ ## note:
184
+ ## (i) :newline => :lf option
185
+ # Reads text natively; normalizes all incoming newlines to strict LF (\n)
186
+ ## (ii) 'bom|' flag
187
+ ## The 'bom|' flag instructs Ruby to drop the BOM bytes if they exist
188
+ File.open( path, 'r:bom|utf-8', newline: :lf) do |f|
168
189
  f.read
169
190
  end
170
191
  end
@@ -199,20 +220,25 @@ end
199
220
  ## add/offer chomp: true/false option or such - why? why not?
200
221
  ## see String.lines in rdoc
201
222
  ##
202
- def read_lines( path )
203
- read_text( path ).lines
223
+ ##
224
+ ## yes, add chomp: true|false option
225
+ ## note - default is chomp: false (keeping the trailing newline)
226
+
227
+ def read_lines( path, chomp: false )
228
+ read_text( path ).lines( chomp: chomp )
204
229
  end
205
230
 
206
- def parse_lines( str )
207
- str.lines
231
+ def parse_lines( str, chomp: false )
232
+ str.lines( chomp: chomp )
208
233
  end
209
234
 
210
- def download_lines( url )
211
- parse_lines( download_text( url ))
235
+ def download_lines( url, chomp: false )
236
+ parse_lines( download_text( url ), chomp: chomp )
212
237
  end
213
238
 
214
239
 
215
240
 
241
+
216
242
  def read_env( path )
217
243
  parse_env( read_text( path ))
218
244
  end
@@ -222,11 +248,17 @@ def parse_env( str )
222
248
  end
223
249
 
224
250
 
251
+
252
+
225
253
  ##
226
254
  ## todo/check - change path to *paths=['./.env']
227
255
  ## and support more files - why? why not?
256
+ ##
257
+ ## note - use File.file? instead of File.exist?
258
+ ## will avoid matching directories!
259
+
228
260
  def load_env( path='./.env' )
229
- if File.exist?( path )
261
+ if File.file?( path )
230
262
  puts "==> loading .env settings..."
231
263
  env = read_env( path )
232
264
  puts " applying .env settings... (merging into ENV)"
@@ -241,6 +273,7 @@ end
241
273
 
242
274
 
243
275
 
276
+
244
277
  ######
245
278
  # add writers
246
279
 
@@ -253,7 +286,9 @@ def write_json( path, data )
253
286
  FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
254
287
 
255
288
  ## note: pretty print/reformat json
256
- File.open( path, 'w:utf-8' ) do |f|
289
+ ## note: on windows ruby translates \n to \r\n
290
+ ## use newline option to use \n everywhere!!
291
+ File.open( path, 'w:utf-8', newline: :lf) do |f|
257
292
  f.write( JSON.pretty_generate( data ))
258
293
  end
259
294
  end
@@ -283,7 +318,9 @@ def write_text( path, text )
283
318
  dirname = File.dirname( path )
284
319
  FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
285
320
 
286
- File.open( path, 'w:utf-8' ) do |f|
321
+ ## note: on windows ruby translates \n to \r\n
322
+ ## use newline option to use \n everywhere!!
323
+ File.open( path, 'w:utf-8', newline: :lf) do |f|
287
324
  f.write( text )
288
325
  end
289
326
  end
@@ -291,7 +328,7 @@ alias_method :write_txt, :write_text
291
328
 
292
329
 
293
330
 
294
- #
331
+
295
332
  # note:
296
333
  # for now write_csv expects array of string arrays
297
334
  # does NOT support array of hashes for now
@@ -300,22 +337,19 @@ def write_csv( path, recs, headers: nil )
300
337
  dirname = File.dirname( path )
301
338
  FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
302
339
 
303
- File.open( path, 'w:utf-8' ) do |f|
304
- if headers
305
- f.write( headers.join(',')) ## e.g. Date,Team 1,FT,HT,Team 2
340
+ ## note: on windows ruby translates \n to \r\n
341
+ ## use newline option to use \n everywhere!!
342
+ File.open( path, 'w:utf-8', newline: :lf) do |f|
343
+ if headers ## e.g. Date,Team 1,FT,HT,Team 2
344
+ f.write( headers.map do |header|
345
+ _escape_csv( header )
346
+ end.join(',') )
306
347
  f.write( "\n" )
307
348
  end
308
349
 
309
350
  recs.each do |values|
310
- ## quote values that incl. a comma
311
- ## todo/fix - add more escape/quote checks - why? why not?
312
- ## check how other csv libs handle value generation
313
351
  buf = values.map do |value|
314
- if value.index(',')
315
- %Q{"#{value}"}
316
- else
317
- value
318
- end
352
+ _escape_csv( value )
319
353
  end.join( ',' )
320
354
 
321
355
  f.write( buf )
@@ -326,9 +360,61 @@ end
326
360
 
327
361
 
328
362
 
363
+ ## quote values that incl. a comma
364
+ ## todo/fix - add more escape/quote checks - why? why not?
365
+ ## check how other csv libs handle value generation
366
+ ##
367
+ ## If a field contains
368
+ ## - a comma (,)
369
+ ## - a double quote (")
370
+ ## - a newline (\r\n)
371
+ ## then wrap the field in quotes
372
+ ## Inside quoted fields, double every double quote (" → "")
373
+
374
+
375
+ def _escape_csv(value)
376
+ ## auto-convert to string or let code fail on nil or such?
377
+ ## or use value.to_str - why? why not?
378
+ value = value.to_s
379
+
380
+ ## note - double double quotes (") for now only
381
+ ## check
382
+ ## - escape newline (lf) as \n or keep it literal - why? why not?
383
+ ## what about \r carriage return (cr)
384
+ ##
385
+ ## add value.match?(/\A\s|\s\z/) ||
386
+ ## to preserve leading/trailing spaces in value ???
387
+ ## e.g. _a_ becomes "_a_" written out
388
+ ##
389
+ ## quote empty strings or keep them empty
390
+ ## what about nil - for now empty string too
391
+ ## add nil_value option e.g. 'n/a' or such
392
+ ## and quote_empty true|false - why? why not?
393
+ if value.include?(',') ||
394
+ value.include?('"') ||
395
+ value.include?("\n") ||
396
+ value.include?("\r")
397
+ '"' + value.gsub('"', '""') + '"'
398
+ else
399
+ value
400
+ end
401
+ end
402
+
403
+
404
+
405
+
406
+
407
+
408
+
329
409
  ######
330
410
  # world wide web (www) support
331
411
 
412
+ ##
413
+ ## keep wget/wget! support - why? why not?
414
+ ## check if used outside anywhere
415
+ ## add deprecated warning - why? why not?
416
+
417
+
332
418
  def wget( url, **opts )
333
419
  Webclient.get( url, **opts )
334
420
  end
@@ -356,4 +442,3 @@ Coco = Cocos
356
442
 
357
443
 
358
444
  puts Cocos.banner ## say hello
359
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-20 00:00:00.000000000 Z
11
+ date: 2026-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: csvreader
@@ -92,14 +92,14 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '4.1'
95
+ version: '4.2'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '4.1'
102
+ version: '4.2'
103
103
  description: cocos (code commons) - auto-include quick-starter prelude & prolog
104
104
  email: gerald.bauer@gmail.com
105
105
  executables: []
@@ -117,6 +117,7 @@ files:
117
117
  - Rakefile
118
118
  - lib/cocos.rb
119
119
  - lib/cocos/env.rb
120
+ - lib/cocos/find_file.rb
120
121
  - lib/cocos/version.rb
121
122
  homepage: https://github.com/rubycocos/cocos
122
123
  licenses:
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  - !ruby/object:Gem::Version
140
141
  version: '0'
141
142
  requirements: []
142
- rubygems_version: 3.4.10
143
+ rubygems_version: 3.5.22
143
144
  signing_key:
144
145
  specification_version: 4
145
146
  summary: cocos (code commons) - auto-include quick-starter prelude & prolog