I am looking at a situation where I'd like to bring some structure to what would be a string in an typical language. And wondering how to use Rebol's parts box to do it.
So let's say I've got a line that looks like this in the original language I'm trying to dialect:
something = ("/foo/mumble" "/foo/${BAR}/baz")
I want to use Rebol's primitives, so certainly a file path. Here is a random example of what I thought of off the top of my head:
something: [%/foo/mumble [%/foo/ BAR %/baz]]
If it were code you'd use REJOIN or COMBINE. But this is not designed to be executed, it's more like a configuration file. You're not supposed to be running arbitrary code, just getting a list of files.
I'm not sure about how feasible it is to stick with strings and yet still have these type as FILE!. Not all characters work in a FILE!, for instance:
>> load "%/foo/${BAR}/baz"
== [%/foo/$ "BAR" /baz]
It makes me wonder what my options are in Rebol data that's supposed to represent a configuration file. I can use plain old strings and do substitutions like other things do. Maybe REWORD with an OBJECT block to represent the environment?
What is the 'reword' function in Rebol and how do I use it?
In any case, I want to know how to represent a filename in a declarative context with environment variable substitutions like this.
I should use file! Your example need "" after %
f: load {%"/foo/${BAR}/baz"}
replace f "${BAR}" "MYVALUE" ;== %/foo/MYVALUE/baz
you could use path! with parens.
the only issue is the root, for which you can use another character to replace the "%" used for files... let's use '! (note this should be a word 'valid character).
when calling to-block on a path! type, it returns each part as its own token... useful.
to-block '!/path/(foo)/file.txt
== [! path (foo) file.txt]
here is a little script which loads three paths and uses parens as a constructed part of the path and uses tags to escape path-illegal characters (like a space!)
environments: make object! [
foo: "FU"
bar: "BR"
]
paths: [
!/path/(foo)/file.txt
!/root/<escape weird chars $>/(bar ".txt")
!/("__" foo)/path/(bar)
]
parse paths [
some [
(print "------" )
set data path! here: ( insert/only here to-block data to-block data )
(out-path: copy %"" )
into [
path-parts: (?? path-parts)
'!
some [
[ set data [word! | tag! | number!] (
append out-path rejoin ["/" to-string data]
)]
|
into [
( append out-path "/")
some [
set data word! ( append out-path rejoin [to-string get in environments data] )
| set data skip ( append out-path rejoin [ to-string data])
]
]
| here: set data skip (to-error rejoin ["invalid path token (" type? data ") here: " mold here])
]
]
(?? out-path)
]
]
Note this works both in Rebol3 and Rebol2
output is as follows:
------
path-parts: [! path (foo) file.txt]
out-path: %/path/FU/file.txt
------
path-parts: [! root <escape weird chars $> (bar ".txt")]
out-path: %/root/escape%20weird%20chars%20$/BR.txt
------
path-parts: [! ("__" foo) path (bar)]
out-path: %/__FU/path/BR
------
Related
My problematic string is like this:
'{\n"test":"AAAA",\n"test2":"BBB\n\n\nBBB"\n}'
I want to parse it as JSON object(Hash) by JSON.parse(jsonstring)
The expecting result is:
{ "test": "AAAA", "test2": "BBB\nB"}
However, I get the error:
JSON::ParserError: 809
I happend to know that indentaion code in jsonstring be escaped,
so I tried this:
escaped_jsonstring = '{\n"test":"AAAA",\n"test2":"BBB\n\n\nBBB"\n}'.gsub(/\R/, '\\n')
JSON.parse(escaped_jsonstring)
I still have JSON::ParserError.
Indentations outside the key or value may cause this error.
How can I remove \n(or \r any indentation code) only outside the key or value in Ruby?
which means,
'{\n"test":"AAAA",\n"test2":"BBB\n\n\nBBB"\n}'
↓
'{"test":"AAAA","test2":"BBB\n\n\nBBB"}'
try this
'{\n"test":"AAAA",\n"test2":"BBB\n\n\nBBB"\n}'.gsub(/\B(\\n)+/, "")
\n" is considered inside boundary (so i use \B), meanwhile "\n is considered outside boundary (\b), (\\n)+ to fix case '...,\n\n\n"test2":...
update
turn out \s\n also be considered an inside boundary ... iam not sure there's other cases ...
for now, the updated version
'{\n"test":"AAAA",\n"test2":"BBB \n\n\n BBB"\n}'
.gsub(/([{,\"]\s*)\B(\\n)+/) { $1 }
better way
i found another way to solve your problem, also using regexp, now i will scan through the input text (valid or invalid json) then filter follow the pair pattern "<key>":"<value>" and don't care anything else outside those pairs, finally output the hash
def format(json)
matches = json.scan(/\"(?<key>[^\"]+)\":\"(?<val>[^\"]+)\",*/)
matches&.to_h
end
format('{\n "test\n parser":"AA\nAA", \n\n"test2":"BBB ? ;\n\n\n BBB" \n}')
# {"test\n parser"=>"AA\nAA", "test2"=>"BBB ? ;\n\n\n BBB"}
I have a file that contains multiple JSON objects that are not separated by comma :
{
"field" : "value",
"another_field": "another_value"
} // no comma
{
"field" : "value"
}
Each of the objects standalone is a valid json object.
Is there a way that I can process this file easily?
I know this is NOT a valid json, but unfortunately this file is being generated by a 3rd party tool. I have no option of changing the way the output looks like.
I can't open a text editor and smart-insert commas / square brackets before the run, since this is an automated process (I also really don't want to write code that opens the file and manipulates it).
In .NET there's a library that has this exact feature :
https://stackoverflow.com/a/29480032/2970729
https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonReader_SupportMultipleContent.htm
Is there anything equivalent in Ruby?
As long as your file is that simple you might want to do something like this:
# content = File.read(filename)
content =<<-EOF
{
"field" : "value",
"another_field": "another_value"
} // no comma
{
"field" : "value"
}
EOF
require 'json'
JSON.parse("[#{content.gsub(/\}.*?\{/m, '},{')}]")
#=> [{"field"=>"value", "another_field"=>"another_value"}, {"field"=>"value"}]
The yajl-ruby gem enables processing concatenated JSON in Ruby. The parser can read from a String or an IO. Each complete object is yielded to a block.
require 'yajl'
File.open 'file.json' do |f|
Yajl.load f do |object|
# do something with object
end
end
See the documentation for other options (buffer size, symbolized keys, etc).
When using:
echo "${env.PRODUCT_NAME}"
it will echo:
MyProdName
When using:
echo "${env.MyProdName_Key}"
it will echo:
123456789
I would like to use something as follows:
echo "${env.${env.PRODUCT_NAME}_Key}"
Is this possible? How?
In Bash this is termed as variable in direction
Try using variables to make it further simplified
PRODUCT_NAME=$(echo "${env.PRODUCT_NAME}")
This would assign PRODUCT_NAME=MyProdName
Similarly
MyProdName=$(echo "${env.MyProdName_Key}")
This would assign MyProdName=123456789
Now when you print PRODUCT_NAME value you will get
echo ${PRODUCT_NAME}
MyProdName
And adding '!' variable indirection will give you the value of another variable values
echo ${!PRODUCT_NAME}
123456789
Maybe this will help you somehow:
def env = [
PRODUCT_NAME:'MyProdName',
MyProdName_Key: 123456789,
]
println "${env[env.PRODUCT_NAME+'_Key']}"
env is Map in the example provided but it works in the exactly same way.
Important note, regardless of how you're deriving variables:
There's no need to use string interpolation if the only value in a
string is a variable itself. This just clutters your code.
Instead of:
echo "${env.PRODUCT_NAME}"
you can do:
echo.PRODUCT_NAME.
Additionally you can grab nested object values dynamically using bracket notation
def obj = [a: '1']
echo obj[a] // outputs '1'
Using these put together, you can do:
def prodName = env.PRODUCT_NAME //will set var prodName to "MyProdName"
echo env[prodName + '_Key'] //gets nested field with key "MyProdName_Key"
(Note: this is similar to Opal's answer, hopefully my breakdown helps)
I have a config file parser written in lua.
I'd like to detect values that are environment variables and change them with os.getenv.
It's probably a bit ambitious because I can have values like
"a string with an embedded ${VARIABLE} in here"
or
"another string with an env $VARIABLE"
And I should probably allow escaping them with double $$ to allow a literal $.
How do I do this?
This is what I have so far, but it isn't right
local envvar = string.match(value, "%$([%w_]+)")
if envvar then
print("Envvar=", envvar)
value = value:gsub("(%$[%w_]+)", os.getenv(envvar))
end
For example, I can't figure out how to use the %b balance option here to properly match { } combinations. And make them optional. How do I make this work robustly?
In fact, I realise it's probably more complicated than this. What if more than one environment variable was specified?
local text = [[
Example: ${LANG}, $TEXTDOMAINDIR, $$10.00, $$LANG, $UNDEFINED
Nested braces: {{${SHELL}}}
]]
text = text:gsub('$%$','\0')
:gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv)
:gsub('%z','$')
print(text)
--> Example: en_US.UTF-8, /usr/share/locale/, $10.00, $LANG, $UNDEFINED
--> Nested braces: {{/bin/bash}}
I need to load a yaml file into Hash,
What should I do?
I would use something like:
hash = YAML.load(File.read("file_path"))
A simpler version of venables' answer:
hash = YAML.load_file("file_path")
Use the YAML module:
http://ruby-doc.org/stdlib-1.9.3/libdoc/yaml/rdoc/YAML.html
node = YAML::parse( <<EOY )
one: 1
two: 2
EOY
puts node.type_id
# prints: 'map'
p node.value['one']
# prints key and value nodes:
# [ #<YAML::YamlNode:0x8220278 #type_id="str", #value="one", #kind="scalar">,
# #<YAML::YamlNode:0x821fcd8 #type_id="int", #value="1", #kind="scalar"> ]'
# Mappings can also be accessed for just the value by accessing as a Hash directly
p node['one']
# prints: #<YAML::YamlNode:0x821fcd8 #type_id="int", #value="1", #kind="scalar">
http://yaml4r.sourceforge.net/doc/page/parsing_yaml_documents.htm
You may run into a problem mentioned at this related question, namely, that the YAML file or stream specifies an object into which the YAML loader will attempt to convert the data into. The problem is that you will need a related Gem that knows about the object in question.
My solution was quite trivial and is provided as an answer to that question. Do this:
yamltext = File.read("somefile","r")
yamltext.sub!(/^--- \!.*$/,'---')
hash = YAML.load(yamltext)
In essence, you strip the object-classifier text from the yaml-text. Then you parse/load it.