Use YAML with variables - ruby-on-rails

Are variables within YAML files possible? For example:
theme:
name: default
css_path: compiled/themes/$theme.name
layout_path: themes/$theme.name
In this example, how can theme: name: default be used in other settings? What is the syntax?

I had this same question, and after a lot of research, it looks like it's not possible.
The answer from cgat is on the right track, but you can't actually concatenate references like that.
Here are things you can do with "variables" in YAML (which are officially called "node anchors" when you set them and "references" when you use them later):
Define a value and use an exact copy of it later:
default: &default_title This Post Has No Title
title: *default_title
{ or }
example_post: &example
title: My mom likes roosters
body: Seriously, she does. And I don't know when it started.
date: 8/18/2012
first_post: *example
second_post:
title: whatever, etc.
For more info, see this section of the wiki page about YAML: http://en.wikipedia.org/wiki/YAML#References
Define an object and use it with modifications later:
default: &DEFAULT
URL: stooges.com
throw_pies?: true
stooges: &stooge_list
larry: first_stooge
moe: second_stooge
curly: third_stooge
development:
<<: *DEFAULT
URL: stooges.local
stooges:
shemp: fourth_stooge
test:
<<: *DEFAULT
URL: test.stooges.qa
stooges:
<<: *stooge_list
shemp: fourth_stooge
This is taken directly from a great demo here: https://gist.github.com/bowsersenior/979804

After some search, I've found a cleaner solution wich use the % operator.
In your YAML file :
key : 'This is the foobar var : %{foobar}'
In your ruby code :
require 'yaml'
file = YAML.load_file('your_file.yml')
foobar = 'Hello World !'
content = file['key']
modified_content = content % { :foobar => foobar }
puts modified_content
And the output is :
This is the foobar var : Hello World !
As #jschorr said in the comment, you can also add multiple variable to the value in the Yaml file :
Yaml :
key : 'The foo var is %{foo} and the bar var is %{bar} !'
Ruby :
# ...
foo = 'FOO'
bar = 'BAR'
# ...
modified_content = content % { :foo => foo, :bar => bar }
Output :
The foo var is FOO and the bar var is BAR !

This is an old post, but I had a similar need and this is the solution I came up with. It is a bit of a hack, but it works and could be refined.
require 'erb'
require 'yaml'
doc = <<-EOF
theme:
name: default
css_path: compiled/themes/<%= data['theme']['name'] %>
layout_path: themes/<%= data['theme']['name'] %>
image_path: <%= data['theme']['css_path'] %>/images
recursive_path: <%= data['theme']['image_path'] %>/plus/one/more
EOF
data = YAML::load("---" + doc)
template = ERB.new(data.to_yaml);
str = template.result(binding)
while /<%=.*%>/.match(str) != nil
str = ERB.new(str).result(binding)
end
puts str
A big downside is that it builds into the yaml document a variable name (in this case, "data") that may or may not exist. Perhaps a better solution would be to use $ and then substitute it with the variable name in Ruby prior to ERB. Also, just tested using hashes2ostruct which allows data.theme.name type notation which is much easier on the eyes. All that is required is to wrap the YAML::load with this
data = hashes2ostruct(YAML::load("---" + doc))
Then your YAML document can look like this
doc = <<-EOF
theme:
name: default
css_path: compiled/themes/<%= data.theme.name %>
layout_path: themes/<%= data.theme.name %>
image_path: <%= data.theme.css_path %>/images
recursive_path: <%= data.theme.image_path %>/plus/one/more
EOF

This is how I was able to configure yaml files to refer to variable.
I have values.yaml where we have root level fields which are used as template variables inside values.yaml
values.yaml
.....
databaseUserPropName: spring.datasource.username
databaseUserName: sa
.....
secrets:
type: Opaque
name: dbservice-secrets
data:
- name: "{{ .Values.databaseUserPropName }}"
value: "{{ .Values.databaseUserName }}"
.....
When referencing these values in secret.yaml, we would use tpl function using syntax {{ tpl TEMPLATE_STRING VALUES }}
secret.yaml
when using inside range i:e iteration
{{ range .Values.deployments.secrets.data }}
{{ tpl .name $ }}: "{{ tpl .value $ }}"
{{ end }}
when directly referring as variable
{{ tpl .Values.deployments.secrets.data.name . }}
{{ tpl .Values.deployments.secrets.data.value . }}
$ - this is global variable and will always point to the root context
. - this variable will point to the root context based on where it used.

if your requirement is like parsing an replacing multiple variable and then use it as a hash/or anything then you can do something like this
require 'yaml'
require 'json'
yaml = YAML.load_file("xxxx.yaml")
blueprint = yaml.to_json % { var_a: "xxxx", var_b: "xxxx"}
hash = JSON.parse(blueprint)
inside the yaml just put variables like this
"%{var_a}"

Rails / ruby frameworks are able to do some templating ... it's frequently used to load env variables ...
# fooz.yml
foo:
bar: <%= $ENV[:some_var] %>
No idea if this works for javascript frameworks as I think that YML format is superset of json and it depends on what reads the yml file for you.
If you can use the template like that or the << >> or the {{ }} styles depending on your reader, after that you just ...
In another yml file ...
# boo.yml
development:
fooz: foo
Which allows you to basically insert a variable as your reference that original file each time which is dynamically set. When reading I was also seeing you can create or open YML files as objects on the fly for several languages which allows you to create a file & chain write a series of YML files or just have them all statically pointing to the dynamically created one.

Related

Jenkins writeYaml not replacing value in yaml file

I have a yaml file with the following structure:
transfers:
- name: xyz
cloud: aws
subheading:
impact: Low
reason: ---
artifacts:
- name: name1
type: type1
source:
hash: a1b2C3dd4 ---> VALUE TO OVERWRITE
I would like to overwrite the existing hash value with a value of the latest GIT_COMMIT.
I have tried the method from the following question: write yaml file in jenkins with groovy. However, the value of hash[0][0] remains unchanged. This is the case even when I replace env.GIT_COMMIT with a test hash string "testHash123". I'm unsure why this is the case?
def filename = ('path/to/file.yaml')
def datas = readYaml file: filename
//change hash
datas.transfers['artifacts'].source.hash[0][0] = env.GIT_COMMIT
writeYaml file: filename, data: datas, overwrite: true
Please try the following.
datas.transfers[0]['artifacts'][0]['source'].hash = env.GIT_COMMIT
The easiest way to figure this out is by printing, so you can understand the structure.
[transfers:[[name:xyz, cloud:aws, subheading:[impact:Low, reason:xxxx], artifacts:[[name:name1, type:type1, source:[hash:a1b2C3dd4]]]]]]
As you can see above the transfer is a sequence, so you need to extract the correct segment with an index.

Show square brackets in yml file

How to generate content in yml file with this format:
required_groups:
- ["member", "cn=serviceboard-users,cn=groups,cn=accounts,dc=int,dc=dostack,dc=io"]
Instead of this format:
required_groups:
- - member
- cn=serviceboard-users,cn=groups,cn=accounts,dc=int,dc=dostack,dc=io
Input:
[["member", "cn=serviceboard-users,cn=groups,cn=accounts,dc=int,dc=dostack,dc=io"]]
Current Code:
File.open("config/ldap/#{Rails.env}.yml", "w") do |file|
data = {}
formatted_groups_array = []
Setting.ldap_users_filter_required_groups.each { |group| formatted_groups_array.push([Setting.ldap_group_membership_attribute.to_s, group.to_s])}
data["required_groups"] = formatted_groups_array
file.write(data.to_yaml)
end
As stated in this answer, the default ruby library Psych does not have many options available, and you cannot customize the output the way you are trying to do.
In the meantime I've found an old possibile custom solution to your problem, but I had to rewrite a small part of it to make it compatible to the most recent ruby version (I am using 2.7.0 for this example). This is my updated script. Basically it will introduce a new way to edit the YAML output using a more concise syntax.
You can use it this way:
File.open("config/ldap/#{Rails.env}.yml", "w") do |file|
data = {}
formatted_groups_array = []
Setting.ldap_users_filter_required_groups.each { |group| formatted_groups_array.push([Setting.ldap_group_membership_attribute.to_s, group.to_s])}
data["required_groups"] = formatted_groups_array
styled_yaml = StyledYAML.inline(data)
file.write(StyledYAML.dump(styled_yaml))
end

Resque scheduler pass hash argument to Job

I have a job that receives a hash argument in its perform method. I want to call it periodically. I defined a CRON to schedule it on the resque_schedule.yml file. I am trying this way:
UpdateInterestHistoryJob:
cron: "0 0 * * * America/Sao_Paulo"
args:
classifier: :SIAPE
However, inside the job, I get the arguments as an array:
["classifier", "SIAPE"]
How do I define it correctly? How do I define the job argument as a hash on the yml file?
I just tested here and a simple dash should be enough:
UpdateInterestHistoryJob:
cron: "* * * * * America/Sao_Paulo"
args:
- classifier: :SIAPE
Also, should you need more arguments in your Resque job, simply place them without further dashes:
UpdateInterestHistoryJob:
cron: "* * * * * America/Sao_Paulo"
args:
- classifier: :SIAPE
another: value
between your Hash and the one included in this example from Ceilingfish I see discrepancy:
You can mark it up like this
feeds:
-
url: 'http://www.google.com'
label: 'default'
Note the spacing is important here. "-" must be indented by a single space (not a tab), and followed by a single space. And url & label must be indented by two spaces (not tabs either).
Additionally this might be helpful: http://www.yaml.org/YAML_for_ruby.html
This is from ww.yaml.org
Simple Inline Hash
Mapping can also be contained on a single line, using the inline syntax. Each key-value pair is separated by a colon, with a comma between each entry in the mapping. Enclose with curly braces.
Yaml
Simple Inline Hash in YAML?
hash: { name: Steve, foo: bar }
Ruby
Simple Inline Hash in Ruby?
{ 'hash' => { 'name' => 'Steve', 'foo' => 'bar' } }
I also include this link from the official YAMLSyntax and there is many explanation about this
Convert Ruby Hash into YAML
https://codedump.io/share/w2EriSJ0wO7T/1/convert-ruby-hash-into-yaml

Phalcon create Link with get parameters

Is there a helper function in phalcon (volt) to create Links to routes with GET-Parameters ? I have pagination-links on which I want to add ?cat=category and ?year=year depending on whether they are set or not.
First
Previous
Next
Last
so that
http://site.tld/tags/xyz?page=2
would become:
http://site.tld/tags/xyz?cat=a&year=b&page=2
or this, if cat is not set or null:
http://site.tld/tags/xyz?year=b&page=2
edit
this way it seems to work:
First
Previous
Next
Last
the rest happens in the controller
IMO it's easier to do that in the controller than using volt.
First, generate the base URL for your pagination links with the URL Service:
$pagingUrl = $this->url->get('tags/' . $tagname->tag);
Now you can get 'cat' and 'year' with something like $this->request->getPost('cat'); to check if it's set and append it to $pagingUrl as GET parameters. Leave a '&page=' at end of the $pagingUrl to easily append the page number later.
Set $page and $pagingUrl as variables for your view so you can easily access it from volt:
$this->view->setVar('page', $page);
$this->view->setVar('pagingUrl', $pagingUrl);
Finally in the view you could use something like that:
{{ link_to(pagingUrl ~ '1', 'First') }}
{{ link_to(pagingUrl ~ page.before, 'Previous') }}
{{ link_to(pagingUrl ~ page.next, 'Next') }}
{{ link_to(pagingUrl ~ page.last, 'Last') }}
EDIT
The solutions above seems hackish because Phalcon designers aimed to work more with clean URLs than explicit GET parameters. If you were passing your parameters this way, your TagController could have an action that supports pagination like this:
class TagController
{
...
public function ListAction($page = 1, $category = 'default-cat', $year = 1997)
{
...
Working that way you can easily create links like these:
tags/list
tags/list/2/stuff
tags/list/9/stuff/2014

How to parse a yaml file into ruby hashs and/or arrays?

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.

Resources