PlantUML: With preprocessor, how to check if key is present? - plantuml

With a dictionary variable, I would like to check the presence of a key
!$domain_color?={
"technical": "#gray"
}
frame input $INPUT_COLOR {
!if "technical" in $domain_color
ABC -> bad
!else
def -> ok
!endif
How I can do that?

I think it's enough to check the value (!if $domain_color.technical):
#startuml
!$domain_color?={ "technical": "#gray" }
'!$domain_color?={ "fred": "#gray" }
frame input $INPUT_COLOR {
!if $domain_color.technical
ABC -> bad
!else
def -> ok
!endif
#enduml
If you uncomment the line defining fred and comment the line defining technical you can see that your diagram changes.

Related

ref to a hash -> its member array -> this array's member's value. How to elegantly access and test?

I want to use an expression like
#{ %$hashref{'key_name'}[1]
or
%$hashref{'key_name}->[1]
to get - and then test - the second (index = 1) member of an array (reference) held by my hash as its "key_name" 's value. But, I can not.
This code here is correct (it works), but I would have liked to combine the two lines that I have marked into one single, efficient, perl-elegant line.
foreach my $tag ('doit', 'source', 'dest' ) {
my $exists = exists( $$thisSectionConfig{$tag});
my #tempA = %$thisSectionConfig{$tag} ; #this line
my $non0len = (#tempA[1] =~ /\w+/ ); # and this line
if ( !$exists || !$non0len) {
print STDERR "No complete \"$tag\" ... etc ... \n";
# program exit ...
}
I know you (the general 'you') can elegantly combine these two lines. Could someone tell me how I could do this?
This code it testing a section of a config file that has been read into a $thisSectionConfig reference-to-a-hash by Config::Simple. Each config file key=value pair then is (I looked with datadumper) held as a two-member array: [0] is the key, [1] is the value. The $tag 's are configuration settings that must be present in the config file sections being processed by this code snippet.
Thank you for any help.
You should read about Arrow operator(->). I guess you want something like this:
foreach my $tag ('doit', 'source', 'dest') {
if(exists $thisSectionConfig -> {$tag}){
my $non0len = ($thisSectionConfig -> {$tag} -> [1] =~ /(\w+)/) ;
}
else {
print STDERR "No complete \"$tag\" ... etc ... \n";
# program exit ...
}

NoneType Buffer interface error in windows 8

I am making a text editor, so you can imagine that there is a lot of manipulating text files. When I double click on the .py file to run it in python.exe in windows, it throws an error of 'NoneType does not support the Buffer interface' As I have only ever heard "buffer" as a programming term in the context of text files, I believe the problem is somewhere in there.
Here is the code:
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
import os
os.chdir(os.getenv('HOME'))
current=None
backup=''
def newfile():
def create_file(entry):
global current
global root
nonlocal create_in
nonlocal name_it
current = open(entry.get(),'w')
root.title(create_in+'/'+current.name+' - Aspus')
name_it.destroy()
create_in=askdirectory()
if create_in!='':
global root
os.chdir(create_in)
name_it=Tk()
name_it.title("Name the File?")
prompt=Label(name_it, text="Enter name for new file:")
prompt.grid(row=0)
entry=Entry(name_it)
entry.grid(row=1)
entry.insert(0, "Untitled.txt")
create=Button(name_it, text="Create", command = lambda: create_file(entry))
create.grid(row=1, column=3)
name_it.mainloop()
def openfile(master):
global current
global backup
opening=askopenfilename()
file=open(opening, 'r')
insert=file.read()
backup=file.read()
file.close()
file=open(opening, 'w')
current=file
master.title(current.name+' - Aspus')
return insert
def savefile(entry):
global current
if current!=None:
current.write(entry.get('1.0', END))
elif current==None:
newfile()
current.write(entry.get('1.0', END))
def ask_save():
global root
global current
global main
if current!=None:
save_exit=askyesnocancel("Save Before Exit?", "Do you want to save before exiting?")
if save_exit==True:
a=current.name
current.close()
current=open(a, 'w')
savefile(main)
current.close()
root.destroy()
exit()
elif save_exit==False:
a=current.name
current.close()
current=open(a, 'w')
current.write(backup)
current.close()
root.destroy()
exit()
elif current==None:
if main.get('0.1', END).strip()!='':
save_exit=askyesnocancel("Save Before Exit?", "Do you want to save before exiting?")
if save_exit==True:
newfile()
savefile()
current.close()
root.destroy()
elif save_exit==False:
root.destroy()
else:
root.destroy()
def setpgwidth():
def adjust(entry):
global main
new_width=entry.get()
try:
main.config(width=int(entry.get()))
except:
showerror("Invalid width", "You entered an invalid width. Expected an integer.")
entry.delete(0, END)
else:
main.pack(expand=Y, fill=Y, side=LEFT)
entry.master.destroy()
width=Tk()
width.title("Set Page Width")
prompt=Label(width, text="Enter new page width:")
prompt.grid(row=0, column=0, columnspan=2)
new=Entry(width)
new.grid(row=1, column=0)
submit=Button(width, text="Submit", command=lambda: adjust(new))
submit.grid(row=1, column=1)
width.mainloop()
root=Tk()
root.title("Aspus Text Editor")
#create main text widget
main=Text(root, wrap=WORD)
main.pack(expand=True, fill=BOTH, side=LEFT)
#create scrollbar
scroll=Scrollbar(root)
scroll.pack(side=RIGHT, fill=Y)
#configure scrollbar
scroll.config(command=main.yview)
main.config(yscrollcommand=scroll.set)
#Creating menus
menu=Menu(root)
root.config(menu=menu)
menu.add_command(label="New File", command=newfile)
menu.add_command(label="Open File", command=lambda: main.insert(END, openfile(root)))
menu.add_command(label="Save File", command=lambda: savefile(main))
formatmenu=Menu(menu)
menu.add_cascade(label="Format", menu=formatmenu)
formatmenu.add_command(label="Set Page Width", command=setpgwidth)
menu.add_command(label="Quit", command=ask_save)
root.protocol("WM_DELETE_WINDOW", ask_save)
root.mainloop()
Does anyone know why this is happening and how to avoid it?
At least part of the problem is these two statements:
insert=file.read()
backup=file.read()
Because read() reads the whole file, backup isn't going to be what you think it is.
The first thing to do is step through the code with pdb, or add some print statements to validate that your data is what you think it is. You're relying heavily on global variables which can easily be changed in an order different than what you expect.

Recursing directories only goes one file deep

I have the following code:
find_info(File) ->
case file:read_file_info(File) of
{ok, Facts} ->
case Facts#file_info.type of
directory -> directory;
regular -> regular
end;
{error,Reason} -> exit(Reason)
end.
find_files(Dir,Flag,Ending,Acc) ->
case file:list_dir(Dir) of
{ok,A} -> find_files_helper(A,Dir,Flag,Acc,Ending);
{_,_} -> Acc
end.
find_files_helper([H|Tail],Dir,Flag,Acc,Ending) ->
A = find_info(filename:absname_join(Dir,H)),
case A of
directory ->
case Flag of
true ->
find_files(filename:absname_join(Dir,H),Flag,Ending,Acc ++ find_files_helper(Tail,Dir,Flag,Acc,Ending));
false -> find_files_helper(Tail,Dir,Flag,Acc,Ending)
end;
regular ->
case filename:extension(H) of
Ending -> find_files_helper(Tail,Dir,Flag,[to_md5_large(H)] ++ Acc, Ending);
_ -> find_files_helper(Tail,Dir,Flag,Acc,Ending)
end;
{error,Reason} -> exit(Reason)
end;
find_files_helper([],_,_,Acc,_) -> Acc.
However whenever I run the find_files/4 the program only goes one file deep before crashing.
Say I have the following directory
home/
a/
ser.erl
b/
c/
file.erl
file2.erl
When run I will get the md5 of file.erl of file2.erl and of ser.erl. However if the directory looks like this:
home/
a/
ser.erl
back.erl
b/
c/
file.erl
file2.erl
Then the whole program crashes. I have spent few good hours looking for what I'm missing here in my logic however I have no idea.
The error message that I get is exception enoent in function p:to_md5_large/1.
In case the md5 is needed here it is:
to_md5_large(File) ->
case file:read_file(File) of
{ok, <<B/binary>>} -> md5_helper(B,erlang:md5_init());
{error,Reason} -> exit(Reason)
end.
md5_helper(<<A:4/binary,B>>,Acc) -> md5_helper(B,erlang:md5_update(Acc,A));
md5_helper(A,Acc) ->
B = erlang:md5_update(Acc,A),
erlang:md5_final(B).
You're getting enoent because you're passing a filename like back.erl to to_md5_large when you're not in the directory where back.erl is located. Try passing the full filename instead. You're already calling filename:absname_join(Dir,H) in find_files_helper, so just save that to a variable and pass that variable instead of H to to_md5_large.
There is a function that does this for you:
fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut
in your case:
Result = filelib:fold_files(Dir, ".*\.erl", true, fun(X,Acc) -> {ok,B} = file:read_file(X), [erlang:md5(B)|Acc] end, []).
[edit]
#Bula:
I didn't answer directly to your question for 2 reasons:
The first one is that, at the time I was writing my answer, you didn't provide the type of error you get. It is very important, with any language, to learn how to get information from error report. In erlang, most of the time, you get the error type an the line where it occurs, looking at the documentation you will have a very helpful information about what was going wrong. By the way, unless you want to manage errors, I discourage you to write things like:
case file:read_file(File) of
{ok, <<B/binary>>} -> md5_helper(B,erlang:md5_init());
{error,Reason} -> exit(Reason)
end.
The following code will do the same, shorter, and you'll get the exact line number where you got an issue (its not the best example in your code, but it's the shorter)
{ok, <<B/binary>>} = file:read_file(File),
md5_helper(B,erlang:md5_init()),
The second is that I find your code too big, with useless helper functions. I think it is important to try to have a concise and readable code, and also to try to use the library function in the right way. For example you are using erlang:md5:init/0, erlang:md5_update/2 and erlang:md5_final/1 while a single call to erlang:md5/1 is enough in your case. The way you use it exists to be able to calculate the md5 when you get the data chunk by chunk, which is not your case, and the way you wrote the helper function does not allow to use this feature.
I don't understand why you want to have a "deployed" version of your code, but I propose you another version where I tried to follow my advices (written directly in the shell, so it need R17+ for the definition of recursive anonymous function) :o)
1> F = fun F(X,D,Ending) ->
1> {ok,StartD} = file:get_cwd(), %% save current directory
1> ok = file:set_cwd(D), %% move to the directory to explore
1> R = case filelib:is_dir(X) of
1> true -> %% if the element to analyze is a directory
1> {ok,Files} = file:list_dir(X), %% getits content
1> [F(Y,X,Ending) || Y <- Files]; %% and recursively analyze all its elements
1> false ->
1> case filelib:is_regular(X) andalso (filename:extension(X) == Ending) of
1> true -> %% if it is a regular file with the right extension
1> {ok,B} = file:read_file(X), %% read it
1> [erlang:md5(B)]; %% and calculate the md5 (must be return in a list
1> %% for consistancy with directory results)
1> false ->
1> [] %% in other cases (symlink, ...) return empty
1> end
1> end,
1> ok = file:set_cwd(StartD), %% restore current directory
1> lists:flatten(R) %% flatten for nicer result
1> end.
#Fun<erl_eval.42.90072148>
2> Md5 = fun(D) -> F(D,D,".erl") end.
#Fun<erl_eval.6.90072148>
3> Md5("C:/My programs/erl6.2/lib/stdlib-2.2").
[<<150,238,21,49,189,164,184,32,42,239,200,52,135,78,12,
112>>,
<<226,53,12,102,125,107,137,149,116,47,50,30,37,13,211,243>>,
<<193,114,120,24,175,27,23,218,7,169,146,8,19,208,73,255>>,
<<227,219,237,12,103,218,175,238,194,103,52,180,132,113,
184,68>>,
<<6,16,213,41,39,138,161,36,184,86,17,183,125,233,20,125>>,
<<23,208,91,76,69,173,159,200,44,72,9,9,50,40,226,27>>,
<<92,8,168,124,230,1,167,199,6,150,239,62,146,119,83,36>>,
<<100,238,68,145,58,22,88,221,179,204,19,26,50,172,142,193>>,
<<253,79,101,49,78,235,151,104,188,223,55,228,163,25,16,
147>>,
<<243,189,25,98,170,97,88,90,174,178,162,19,249,141,94,60>>,
<<237,85,6,153,218,60,23,104,162,112,65,69,148,90,15,240>>,
<<225,48,238,193,120,43,124,63,156,207,11,4,254,96,250,204>>,
<<67,254,107,82,106,87,36,119,140,78,216,142,66,225,8,40>>,
<<185,246,227,162,211,133,212,10,174,21,204,75,128,125,
200,...>>,
<<234,191,210,59,62,148,130,187,60,0,187,124,150,213,...>>,
<<199,231,45,34,185,9,231,162,187,130,134,246,54,...>>,
<<157,226,127,87,191,151,81,50,19,116,96,121,...>>,
<<15,59,143,114,184,207,96,164,155,44,238,...>>,
<<176,139,190,30,114,248,0,144,201,14,...>>,
<<169,79,218,157,20,10,20,146,12,...>>,
<<131,25,76,110,14,183,5,103,...>>,
<<91,197,189,2,48,142,67,...>>,
<<94,202,72,164,129,237,...>>,
<<"^NQÙ¡8hÿèkàå"...>>,<<"ðÙ.Q"...>>,
<<150,101,76,...>>,
<<"A^ÏrÔ"...>>,<<"¹"...>>,<<...>>|...]
4>

How do I check for pangrams in a line in ruby?

Some of you may notice I'm already back with the same painful code already. I'm not sure if the other question is still open or not once I accept an answer.
Now the problem is a little simpler. I found some code that checked for pangrams. It use to be def pangram?('sentence') but I needed line to go in there so I tried changing it to def pangram?(line). It doesn't seem to mesh well with my coding style and doesn't work. I tried to use .contain('a' . . 'z') to check for a pangram but someone I know tried that and it didn't work. Also google isn't much help either.
Any ideas for how I could check for pangrams in an if stmt?
# To change this template, choose Tools | Templates
# and open the template in the editor
# This program reads a file line by line,
#separating lines by writing into certain text files.
#PPQ - Pangrams, Palindromes, and Quotes
class PPQ
def pangram?(line)
unused_letters = ('a'..'z').to_a - line.downcase.chars.to_a
unused_letters.empty?
end
def categorize
file_pangram = File.new('pangram.txt', 'w')
file_palindrome = File.new('palindrome.txt', 'w')
file_quotes = File.new('quotes.txt','w')
File.open('ruby1.txt','r') do |file|
while line = file.gets
if(line.reverse == line)
file_palindrome.write line
elsif(pangram?(line)== true)
file_pangram.write line
else
file_quotes.write line
end
end
end
file.close
file_pangram.close
file_palindrome.close
file_quotes.close
end
end
my_ruby_assignment = PPQ.new
my_ruby_assignment.categorize
I'm partial to simpler syntax, something like
def pangram?(line)
('a'..'z').all? { |word| line.downcase.include? (word) }
end
if pangram?(line) then file_pangram.write line end
def pangram?(string)
str = string.chars.map(&:downcase)
letters =('a'..'z').to_a
result = true
letters.each do |l|
if !(str.include? l.downcase)
result = false
break
end
end
result
end

Compact C Folding in Vim

I'm trying to make a simple Vim script that would create very compact top-level folds for c files. Ideally, if it was run on this code:
static void funca(...)
{
...
}
/* Example comment */
static void funcb(...)
{
...
}
Then it would create folds which would look like this when closed:
+-- x Lines: static void funca(...)----------------------
+-- x Lines: static void funcb(...)----------------------
So basically it would be like foldmethod=syntax with foldlevel=1, except that each fold would start one line further up, and would extend further down to include all following blank lines.
I know how to make one of these folds (assuming foldmethod=manual):
/^{<cr>kVnn?^$<cr>zf
But I'm not sure how to put it into a function. This is my effort:
function Cfold()
set foldmethod=manual " Manual folds
ggzE " Delete all folds
while (/^{<cr>) " Somehow loop through each match
kVnn?^$<cr>zf " This would work fine except for the last function
endwhile
endfunction
map <Leader>f :call Cfold()<cr>
But it isn't valid, I'm not entirely sure how functions work. Also, it won't work for the last function in the file, since it won't find '^{' again. If someone could help me get this working, and somehow add a case for the last function in a file, I would be extremely grateful.
Thanks in advance :)
You can create folds programmatically using the foldexpr and foldtext. Try this, though you may have to tweak CFoldLevel so it doesn't swallow non-function parts of the code:
function! CFoldLevel(lnum)
let line = getline(a:lnum)
if line =~ '^/\*'
return '>1' " A new fold of level 1 starts here.
else
return '1' " This line has a foldlevel of 1.
endif
endfunction
function! CFoldText()
" Look through all of the folded text for the function signature.
let signature = ''
let i = v:foldstart
while signature == '' && i < v:foldend
let line = getline(i)
if line =~ '\w\+(.*)$'
let signature = line
endif
let i = i + 1
endwhile
" Return what the fold should show when folded.
return '+-- ' . (v:foldend - v:foldstart) . ' Lines: ' . signature . ' '
endfunction
function! CFold()
set foldenable
set foldlevel=0
set foldmethod=expr
set foldexpr=CFoldLevel(v:lnum)
set foldtext=CFoldText()
set foldnestmax=1
endfunction
See :help 'foldexpr' for more details.

Resources