cocos 0.3.1 → 0.4.1
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 +4 -4
- data/CHANGELOG.md +2 -1
- data/Manifest.txt +1 -0
- data/README.md +5 -8
- data/Rakefile +1 -1
- data/lib/cocos/env.rb +22 -17
- data/lib/cocos/find_file.rb +45 -0
- data/lib/cocos/version.rb +2 -4
- data/lib/cocos.rb +155 -91
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51832aaf3ccd7164304a1c4652108b89d7651d2d7358e80c8f0d24fa46f59f69
|
|
4
|
+
data.tar.gz: 3236f78ad3ced02d1ac42a047a0ce11eaf60017063b61c2c1480786e3513ddc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11206d11694da8a98bf33b2c444975ec3e195e4c29348f761945fba044fe582d5ab885bb8a5d3057f3ce5589ba288bc14b9d2b456e0003fa74537ac5efa3d0a1
|
|
7
|
+
data.tar.gz: 8f9e09293599531c4fd4482295fc75e0d7aeea95ffe5de62f7c2bc4d2e84a4e8b643109682accdb98b3295e30e89265f0aa6b36dbcaf70e25adfd8488ea6ba8c
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
data/README.md
CHANGED
|
@@ -87,8 +87,7 @@ And so on.
|
|
|
87
87
|
|
|
88
88
|
_Read / parse convenience short-cut helpers_
|
|
89
89
|
|
|
90
|
-
`read_blob( path )`
|
|
91
|
-
also known as `read_binary` or `read_bin`
|
|
90
|
+
`read_blob( path )`
|
|
92
91
|
|
|
93
92
|
|
|
94
93
|
`read_text( path )` <br>
|
|
@@ -98,14 +97,14 @@ also known as `read_txt`
|
|
|
98
97
|
`read_lines( path )`
|
|
99
98
|
|
|
100
99
|
|
|
101
|
-
|
|
102
100
|
`read_json( path )` / `parse_json( str )`
|
|
103
101
|
|
|
104
102
|
|
|
105
|
-
`read_yaml( path )` / `parse_yaml( str )`
|
|
103
|
+
`read_yaml( path )` / `parse_yaml( str )` <br>
|
|
104
|
+
also known as `read_yml` / `parse_yml`
|
|
106
105
|
|
|
107
106
|
|
|
108
|
-
`read_csv( path
|
|
107
|
+
`read_csv( path )` / `parse_csv( str )`
|
|
109
108
|
|
|
110
109
|
note: comma-separated values (.csv) reading & parsing service
|
|
111
110
|
brought to you by the [**csvreader library / gem »**](https://github.com/rubycocos/csvreader/tree/master/csvreader)
|
|
@@ -113,8 +112,7 @@ brought to you by the [**csvreader library / gem »**](https://github.com/rubyco
|
|
|
113
112
|
|
|
114
113
|
`read_data( path )` / `parse_data( str )`
|
|
115
114
|
|
|
116
|
-
note: alternate
|
|
117
|
-
|
|
115
|
+
note: alternate csv reader / parser; reads data WITHOUT headers, that is, named columns - returns data array not named hash (table)
|
|
118
116
|
|
|
119
117
|
|
|
120
118
|
|
|
@@ -125,7 +123,6 @@ brought to you by the [**tabreader library / gem »**](https://github.com/rubyco
|
|
|
125
123
|
|
|
126
124
|
|
|
127
125
|
|
|
128
|
-
|
|
129
126
|
`read_ini( path )` / `parse_ini( str )` <br>
|
|
130
127
|
also known as `read_conf / parse_conf`
|
|
131
128
|
|
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 = '
|
|
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
|
|
|
@@ -19,54 +19,59 @@ module EnvParser
|
|
|
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
|
-
|
|
35
|
+
LINE_RE = /\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
44
|
/x
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
## use a parser class - why? why not?
|
|
47
|
+
def self.parse( text )
|
|
48
|
+
h = {}
|
|
46
49
|
|
|
47
|
-
|
|
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
|
|
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=
|
|
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
|
-
|
|
66
|
+
else
|
|
62
67
|
raise ParseError, "line #{lineno} - unknown line type; cannot parse >#{line}<"
|
|
63
|
-
|
|
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,45 @@
|
|
|
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 Errorno::ENOENT, "file <#{name}> 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
|
+
##
|
|
30
|
+
## todo/check - expand path and use File.realpath too?
|
|
31
|
+
## or keep "simple" File.join ?
|
|
32
|
+
|
|
33
|
+
def find_file( name, path: )
|
|
34
|
+
return name if File.file?( name )
|
|
35
|
+
|
|
36
|
+
path.each do |dir|
|
|
37
|
+
filepath = File.join( dir, name )
|
|
38
|
+
return filepath if File.file?( filepath )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
nil ## return nil if not found
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
end # module Kernel
|
data/lib/cocos/version.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
module Cocos
|
|
3
3
|
MAJOR = 0 ## todo: namespace inside version or something - why? why not??
|
|
4
|
-
MINOR =
|
|
4
|
+
MINOR = 4
|
|
5
5
|
PATCH = 1
|
|
6
6
|
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
|
7
7
|
|
|
@@ -14,8 +14,6 @@ module Cocos
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.root
|
|
17
|
-
File.expand_path( File.dirname(File.dirname(__FILE__) ))
|
|
17
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__) )))
|
|
18
18
|
end
|
|
19
|
-
|
|
20
19
|
end # module Cocos
|
|
21
|
-
|
data/lib/cocos.rb
CHANGED
|
@@ -29,8 +29,12 @@ require 'webclient'
|
|
|
29
29
|
|
|
30
30
|
#####################
|
|
31
31
|
# our own code
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
require_relative 'cocos/version' # note: let version always go first
|
|
33
|
+
require_relative 'cocos/env' ## e.g. EnvParser
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
require_relative 'cocos/find_file'
|
|
37
|
+
|
|
34
38
|
|
|
35
39
|
###
|
|
36
40
|
## read/parse convenience/helper shortcuts
|
|
@@ -43,138 +47,153 @@ module Kernel
|
|
|
43
47
|
################
|
|
44
48
|
# private helpers - keep along here - why? why not?
|
|
45
49
|
|
|
46
|
-
##### check if path starts with http:// or https://
|
|
47
|
-
## if yes, assume it's a download
|
|
48
|
-
DOWNLOAD_RX = %r{^https?://}i
|
|
49
50
|
|
|
50
|
-
##
|
|
51
|
-
##
|
|
52
|
-
def _download?( path )
|
|
53
|
-
!! DOWNLOAD_RX.match( path )
|
|
54
|
-
end
|
|
51
|
+
## todo: add symbolize options a la read_json? - why? why not?
|
|
52
|
+
## add sep options
|
|
55
53
|
|
|
54
|
+
def read_csv( path, sep: nil )
|
|
55
|
+
opts = {}
|
|
56
|
+
opts[:sep] = sep if sep
|
|
56
57
|
|
|
58
|
+
CsvHash.read( path, **opts )
|
|
59
|
+
end
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
parse_csv( _wget!( path ).text,
|
|
64
|
-
headers: headers )
|
|
65
|
-
else
|
|
66
|
-
if headers
|
|
67
|
-
CsvHash.read( path )
|
|
68
|
-
else
|
|
69
|
-
Csv.read( path )
|
|
70
|
-
end
|
|
71
|
-
end
|
|
61
|
+
def parse_csv( str, sep: nil )
|
|
62
|
+
opts = {}
|
|
63
|
+
opts[:sep] = sep if sep
|
|
64
|
+
|
|
65
|
+
CsvHash.parse( str, **opts )
|
|
72
66
|
end
|
|
73
67
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
|
|
69
|
+
## note - use explicit download for now
|
|
70
|
+
##
|
|
71
|
+
def download_csv( url, sep: nil )
|
|
72
|
+
opts = {}
|
|
73
|
+
opts[:sep] = sep if sep
|
|
74
|
+
|
|
75
|
+
parse_csv( download_text( url ),
|
|
76
|
+
**opts )
|
|
80
77
|
end
|
|
81
78
|
|
|
82
79
|
|
|
80
|
+
|
|
83
81
|
### note: use read_data / parse_data
|
|
84
82
|
## for alternate shortcut for read_csv / parse_csv w/ headers: false
|
|
85
83
|
## returning arrays of strings
|
|
86
84
|
def read_data( path )
|
|
87
|
-
|
|
88
|
-
read_data( _wget!( path ).text )
|
|
89
|
-
else
|
|
90
|
-
Csv.read( path )
|
|
91
|
-
end
|
|
85
|
+
Csv.read( path )
|
|
92
86
|
end
|
|
93
87
|
|
|
94
88
|
def parse_data( str )
|
|
95
89
|
Csv.parse( str )
|
|
96
90
|
end
|
|
97
91
|
|
|
92
|
+
def download_data( url )
|
|
93
|
+
parse_data( download_text( url ))
|
|
94
|
+
end
|
|
95
|
+
|
|
98
96
|
|
|
99
97
|
|
|
100
98
|
def read_tab( path )
|
|
101
|
-
|
|
102
|
-
parse_tab( _wget!( path ).text )
|
|
103
|
-
else
|
|
104
|
-
Tab.read( path )
|
|
105
|
-
end
|
|
99
|
+
Tab.read( path )
|
|
106
100
|
end
|
|
107
101
|
|
|
108
102
|
def parse_tab( str )
|
|
109
103
|
Tab.parse( str )
|
|
110
104
|
end
|
|
111
105
|
|
|
106
|
+
def download_tab( url )
|
|
107
|
+
parse_tab( download_text( url ))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
112
111
|
|
|
113
112
|
## todo: add symbolize options ???
|
|
114
113
|
def read_json( path )
|
|
115
|
-
|
|
114
|
+
parse_json( read_text( path ))
|
|
116
115
|
end
|
|
117
116
|
|
|
118
117
|
def parse_json( str )
|
|
119
118
|
JSON.parse( str )
|
|
120
119
|
end
|
|
121
120
|
|
|
121
|
+
def download_json( url )
|
|
122
|
+
parse_json( download_text( url ))
|
|
123
|
+
end
|
|
124
|
+
|
|
122
125
|
|
|
123
126
|
### todo/check: use parse_safeyaml or such? (is default anyway?) - why? why not?
|
|
124
127
|
def read_yaml( path )
|
|
125
|
-
|
|
128
|
+
parse_yaml( read_text( path ))
|
|
126
129
|
end
|
|
127
130
|
|
|
128
131
|
def parse_yaml( str )
|
|
129
132
|
YAML.load( str )
|
|
130
133
|
end
|
|
131
134
|
|
|
135
|
+
def download_yaml( url )
|
|
136
|
+
parse_yaml( download_text( url ))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
## keep yml alias - why? why not?
|
|
140
|
+
alias_method :read_yml, :read_yaml
|
|
141
|
+
alias_method :parse_yml, :parse_yaml
|
|
142
|
+
alias_method :download_yml, :download_yaml
|
|
143
|
+
|
|
132
144
|
|
|
133
145
|
def read_ini( path )
|
|
134
|
-
|
|
146
|
+
parse_ini( read_text( path ))
|
|
135
147
|
end
|
|
136
148
|
|
|
137
149
|
def parse_ini( str )
|
|
138
150
|
INI.load( str )
|
|
139
151
|
end
|
|
140
152
|
|
|
141
|
-
|
|
142
|
-
|
|
153
|
+
def download_ini( url )
|
|
154
|
+
parse_ini( download_text( url ))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
alias_method :read_conf, :read_ini
|
|
158
|
+
alias_method :parse_conf, :parse_ini
|
|
159
|
+
alias_method :download_conf, :download_ini
|
|
143
160
|
|
|
144
161
|
|
|
145
162
|
|
|
146
163
|
|
|
147
164
|
def read_text( path )
|
|
148
|
-
if _download?( path )
|
|
149
|
-
_wget!( path ).text
|
|
150
|
-
else
|
|
151
165
|
## todo/check: add universal newline mode or such?
|
|
152
166
|
## e.g. will always convert all
|
|
153
167
|
## newline variants (\n|\r|\n\r) to "universal" \n only
|
|
154
168
|
##
|
|
155
169
|
## add r:bom - why? why not?
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
txt
|
|
160
|
-
end
|
|
170
|
+
File.open( path, 'r:utf-8' ) do |f|
|
|
171
|
+
f.read
|
|
172
|
+
end
|
|
161
173
|
end
|
|
162
|
-
|
|
174
|
+
|
|
175
|
+
def download_text( url )
|
|
176
|
+
wget!( url ).text
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
alias_method :read_txt, :read_text
|
|
180
|
+
alias_method :download_txt, :download_text
|
|
181
|
+
|
|
163
182
|
|
|
164
183
|
|
|
165
184
|
def read_blob( path )
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
blob = File.open( path, 'rb' ) do |f|
|
|
170
|
-
f.read
|
|
171
|
-
end
|
|
172
|
-
blob
|
|
173
|
-
end
|
|
185
|
+
File.open( path, 'rb' ) do |f|
|
|
186
|
+
f.read
|
|
187
|
+
end
|
|
174
188
|
end
|
|
175
|
-
alias_method :read_binary, :read_blob
|
|
176
|
-
alias_method :read_bin, :read_blob
|
|
189
|
+
## alias_method :read_binary, :read_blob
|
|
190
|
+
## alias_method :read_bin, :read_blob
|
|
177
191
|
|
|
192
|
+
def download_blob( url )
|
|
193
|
+
wget!( url ).blob
|
|
194
|
+
end
|
|
195
|
+
## alias_method :download_binary, :download_blob
|
|
196
|
+
## alias_method :download_bin, :download_blob
|
|
178
197
|
|
|
179
198
|
|
|
180
199
|
|
|
@@ -191,10 +210,14 @@ def parse_lines( str )
|
|
|
191
210
|
str.lines
|
|
192
211
|
end
|
|
193
212
|
|
|
213
|
+
def download_lines( url )
|
|
214
|
+
parse_lines( download_text( url ))
|
|
215
|
+
end
|
|
216
|
+
|
|
194
217
|
|
|
195
218
|
|
|
196
219
|
def read_env( path )
|
|
197
|
-
|
|
220
|
+
parse_env( read_text( path ))
|
|
198
221
|
end
|
|
199
222
|
|
|
200
223
|
def parse_env( str )
|
|
@@ -202,11 +225,17 @@ def parse_env( str )
|
|
|
202
225
|
end
|
|
203
226
|
|
|
204
227
|
|
|
228
|
+
|
|
229
|
+
|
|
205
230
|
##
|
|
206
231
|
## todo/check - change path to *paths=['./.env']
|
|
207
232
|
## and support more files - why? why not?
|
|
233
|
+
##
|
|
234
|
+
## note - use File.file? instead of File.exist?
|
|
235
|
+
## will avoid matching directories!
|
|
236
|
+
|
|
208
237
|
def load_env( path='./.env' )
|
|
209
|
-
if File.
|
|
238
|
+
if File.file?( path )
|
|
210
239
|
puts "==> loading .env settings..."
|
|
211
240
|
env = read_env( path )
|
|
212
241
|
puts " applying .env settings... (merging into ENV)"
|
|
@@ -251,8 +280,8 @@ def write_blob( path, blob )
|
|
|
251
280
|
f.write( blob )
|
|
252
281
|
end
|
|
253
282
|
end
|
|
254
|
-
alias_method :write_binary, :write_blob
|
|
255
|
-
alias_method :write_bin, :write_blob
|
|
283
|
+
# alias_method :write_binary, :write_blob
|
|
284
|
+
# alias_method :write_bin, :write_blob
|
|
256
285
|
|
|
257
286
|
|
|
258
287
|
def write_text( path, text )
|
|
@@ -271,54 +300,90 @@ alias_method :write_txt, :write_text
|
|
|
271
300
|
|
|
272
301
|
|
|
273
302
|
|
|
274
|
-
|
|
303
|
+
|
|
275
304
|
# note:
|
|
276
305
|
# for now write_csv expects array of string arrays
|
|
277
|
-
# does NOT support array of hashes for now
|
|
306
|
+
# does NOT support array of hashes for now
|
|
278
307
|
|
|
279
308
|
def write_csv( path, recs, headers: nil )
|
|
280
309
|
dirname = File.dirname( path )
|
|
281
310
|
FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
|
|
282
311
|
|
|
283
312
|
File.open( path, 'w:utf-8' ) do |f|
|
|
284
|
-
if headers
|
|
285
|
-
f.write( headers.
|
|
313
|
+
if headers ## e.g. Date,Team 1,FT,HT,Team 2
|
|
314
|
+
f.write( headers.map do |header|
|
|
315
|
+
_escape_csv( header )
|
|
316
|
+
end.join(',') )
|
|
286
317
|
f.write( "\n" )
|
|
287
|
-
end
|
|
318
|
+
end
|
|
288
319
|
|
|
289
320
|
recs.each do |values|
|
|
290
|
-
## quote values that incl. a comma
|
|
291
|
-
## todo/fix - add more escape/quote checks - why? why not?
|
|
292
|
-
## check how other csv libs handle value generation
|
|
293
321
|
buf = values.map do |value|
|
|
294
|
-
|
|
295
|
-
%Q{"#{value}"}
|
|
296
|
-
else
|
|
297
|
-
value
|
|
298
|
-
end
|
|
322
|
+
_escape_csv( value )
|
|
299
323
|
end.join( ',' )
|
|
300
|
-
|
|
324
|
+
|
|
301
325
|
f.write( buf )
|
|
302
326
|
f.write( "\n" )
|
|
303
|
-
end
|
|
327
|
+
end
|
|
304
328
|
end
|
|
305
329
|
end
|
|
306
330
|
|
|
307
331
|
|
|
308
332
|
|
|
333
|
+
## quote values that incl. a comma
|
|
334
|
+
## todo/fix - add more escape/quote checks - why? why not?
|
|
335
|
+
## check how other csv libs handle value generation
|
|
336
|
+
##
|
|
337
|
+
## If a field contains
|
|
338
|
+
## - a comma (,)
|
|
339
|
+
## - a double quote (")
|
|
340
|
+
## - a newline (\r\n)
|
|
341
|
+
## then wrap the field in quotes
|
|
342
|
+
## Inside quoted fields, double every double quote (" → "")
|
|
343
|
+
|
|
344
|
+
def _escape_csv(value)
|
|
345
|
+
## auto-convert to string or let code fail on nil or such?
|
|
346
|
+
value = value.to_s
|
|
347
|
+
|
|
348
|
+
## note - double double quotes (") for now only
|
|
349
|
+
## check
|
|
350
|
+
## - escape newline (lf) as \n or keep it literal - why? why not?
|
|
351
|
+
## what about \r carriage return (cr)
|
|
352
|
+
##
|
|
353
|
+
## add value.match?(/\A\s|\s\z/) ||
|
|
354
|
+
## to preserve leading/trailing spaces in value ???
|
|
355
|
+
## e.g. _a_ becomes "_a_" written out
|
|
356
|
+
##
|
|
357
|
+
## quote empty strings or keep them empty
|
|
358
|
+
## what about nil - for now empty string too
|
|
359
|
+
## add nil_value option e.g. 'n/a' or such
|
|
360
|
+
## and quote_empty true|false - why? why not?
|
|
361
|
+
if value.include?(',') ||
|
|
362
|
+
value.include?('"') ||
|
|
363
|
+
value.include?("\n") ||
|
|
364
|
+
value.include?("\r")
|
|
365
|
+
'"' + value.gsub('"', '""') + '"'
|
|
366
|
+
else
|
|
367
|
+
value
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
309
377
|
######
|
|
310
378
|
# world wide web (www) support
|
|
311
379
|
|
|
312
|
-
def wget( url, **
|
|
313
|
-
Webclient.get( url, **
|
|
380
|
+
def wget( url, **opts )
|
|
381
|
+
Webclient.get( url, **opts )
|
|
314
382
|
end
|
|
315
383
|
## add alias www_get or web_get - why? why not?
|
|
316
384
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
## private helper - make public -why? why not?
|
|
320
|
-
def _wget!( url, **kwargs )
|
|
321
|
-
res = Webclient.get( url, **kwargs )
|
|
385
|
+
def wget!( url, **opts )
|
|
386
|
+
res = Webclient.get( url, **opts )
|
|
322
387
|
|
|
323
388
|
## check/todo - use a different exception/error - keep RuntimeError - why? why not?
|
|
324
389
|
raise RuntimeError, "HTTP #{res.status.code} - #{res.status.message}" if res.status.nok?
|
|
@@ -339,4 +404,3 @@ Coco = Cocos
|
|
|
339
404
|
|
|
340
405
|
|
|
341
406
|
puts Cocos.banner ## say hello
|
|
342
|
-
|
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
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gerald Bauer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: csvreader
|
|
@@ -92,16 +92,16 @@ dependencies:
|
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '4.
|
|
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.
|
|
102
|
+
version: '4.2'
|
|
103
103
|
description: cocos (code commons) - auto-include quick-starter prelude & prolog
|
|
104
|
-
email:
|
|
104
|
+
email: gerald.bauer@gmail.com
|
|
105
105
|
executables: []
|
|
106
106
|
extensions: []
|
|
107
107
|
extra_rdoc_files:
|
|
@@ -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.
|
|
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
|